feat: finish list destructure in when matches
This commit is contained in:
parent
8cbdf97d22
commit
de9302a877
|
@ -49,7 +49,8 @@ pub enum IR {
|
||||||
|
|
||||||
Tail {
|
Tail {
|
||||||
scope: Vec<u64>,
|
scope: Vec<u64>,
|
||||||
count: usize,
|
name: String,
|
||||||
|
prev_tail_name: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
ListAccessor {
|
ListAccessor {
|
||||||
|
@ -58,10 +59,12 @@ pub enum IR {
|
||||||
tail: bool,
|
tail: bool,
|
||||||
},
|
},
|
||||||
|
|
||||||
// func(x, other(y))
|
ListExpose {
|
||||||
//[ Define(3) x definition *lam_body* -> [ (x [ (func [ func x [ (y [ other y ]) *definition* ] ]) *definition* ]) *definition* ], Define func -> [ (func [ func x [ (y [ other y ]) *definition* ] ]) *definition* ] , Call func -> [ func x [ (y [ other y ]) *definition* ] ], Var x -> x, Define y -> [ (y [ other y ]) *definition* ], Call other -> [ other y ], Var y -> y]
|
scope: Vec<u64>,
|
||||||
|
tail_head_names: Vec<(String, String)>,
|
||||||
|
tail: Option<(String, String)>,
|
||||||
|
},
|
||||||
|
|
||||||
// [y_varCall other_var Call Call x_defCall func_var ]
|
|
||||||
Call {
|
Call {
|
||||||
scope: Vec<u64>,
|
scope: Vec<u64>,
|
||||||
count: usize,
|
count: usize,
|
||||||
|
@ -128,19 +131,25 @@ pub enum IR {
|
||||||
// },
|
// },
|
||||||
When {
|
When {
|
||||||
scope: Vec<u64>,
|
scope: Vec<u64>,
|
||||||
count: usize,
|
|
||||||
tipo: Arc<Type>,
|
tipo: Arc<Type>,
|
||||||
subject_name: String,
|
subject_name: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
Clause {
|
Clause {
|
||||||
scope: Vec<u64>,
|
scope: Vec<u64>,
|
||||||
count: usize,
|
|
||||||
tipo: Arc<Type>,
|
tipo: Arc<Type>,
|
||||||
subject_name: String,
|
subject_name: String,
|
||||||
complex_clause: bool,
|
complex_clause: bool,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
ListClause {
|
||||||
|
scope: Vec<u64>,
|
||||||
|
tipo: Arc<Type>,
|
||||||
|
tail_name: String,
|
||||||
|
complex_clause: bool,
|
||||||
|
next_tail_name: Option<String>,
|
||||||
|
},
|
||||||
|
|
||||||
ClauseGuard {
|
ClauseGuard {
|
||||||
scope: Vec<u64>,
|
scope: Vec<u64>,
|
||||||
subject_name: String,
|
subject_name: String,
|
||||||
|
@ -157,7 +166,6 @@ pub enum IR {
|
||||||
|
|
||||||
If {
|
If {
|
||||||
scope: Vec<u64>,
|
scope: Vec<u64>,
|
||||||
count: usize,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
Constr {
|
Constr {
|
||||||
|
@ -234,6 +242,7 @@ impl IR {
|
||||||
| IR::List { scope, .. }
|
| IR::List { scope, .. }
|
||||||
| IR::Tail { scope, .. }
|
| IR::Tail { scope, .. }
|
||||||
| IR::ListAccessor { scope, .. }
|
| IR::ListAccessor { scope, .. }
|
||||||
|
| IR::ListExpose { scope, .. }
|
||||||
| IR::Call { scope, .. }
|
| IR::Call { scope, .. }
|
||||||
| IR::Builtin { scope, .. }
|
| IR::Builtin { scope, .. }
|
||||||
| IR::BinOp { scope, .. }
|
| IR::BinOp { scope, .. }
|
||||||
|
@ -245,6 +254,7 @@ impl IR {
|
||||||
| IR::Lam { scope, .. }
|
| IR::Lam { scope, .. }
|
||||||
| IR::When { scope, .. }
|
| IR::When { scope, .. }
|
||||||
| IR::Clause { scope, .. }
|
| IR::Clause { scope, .. }
|
||||||
|
| IR::ListClause { scope, .. }
|
||||||
| IR::ClauseGuard { scope, .. }
|
| IR::ClauseGuard { scope, .. }
|
||||||
| IR::Discard { scope }
|
| IR::Discard { scope }
|
||||||
| IR::Finally { scope }
|
| IR::Finally { scope }
|
||||||
|
|
|
@ -13,10 +13,10 @@ use uplc::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{ArgName, AssignmentKind, BinOp, DataType, Function, Pattern, Span, TypedArg},
|
ast::{ArgName, AssignmentKind, BinOp, Clause, DataType, Function, Pattern, Span, TypedArg},
|
||||||
expr::TypedExpr,
|
expr::TypedExpr,
|
||||||
ir::IR,
|
ir::IR,
|
||||||
tipo::{self, Type, TypeInfo, ValueConstructor, ValueConstructorVariant},
|
tipo::{self, PatternConstructor, Type, TypeInfo, ValueConstructor, ValueConstructorVariant},
|
||||||
uplc::{DataTypeKey, FunctionAccessKey},
|
uplc::{DataTypeKey, FunctionAccessKey},
|
||||||
IdGenerator,
|
IdGenerator,
|
||||||
};
|
};
|
||||||
|
@ -37,10 +37,13 @@ pub struct FuncComponents {
|
||||||
|
|
||||||
// }
|
// }
|
||||||
|
|
||||||
pub struct ClauseComplexity {
|
#[derive(Clone, Debug)]
|
||||||
subject_var_name: String,
|
pub struct ClauseProperties {
|
||||||
needs_subject_var: bool,
|
clause_var_name: String,
|
||||||
|
needs_constr_var: bool,
|
||||||
is_complex_clause: bool,
|
is_complex_clause: bool,
|
||||||
|
current_index: usize,
|
||||||
|
original_subject_name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CodeGenerator<'a> {
|
pub struct CodeGenerator<'a> {
|
||||||
|
@ -83,11 +86,11 @@ impl<'a> CodeGenerator<'a> {
|
||||||
|
|
||||||
self.build_ir(&body, &mut ir_stack, scope);
|
self.build_ir(&body, &mut ir_stack, scope);
|
||||||
|
|
||||||
println!("{ir_stack:#?}");
|
println!("INITIAL: {ir_stack:#?}");
|
||||||
|
|
||||||
self.define_ir(&mut ir_stack);
|
self.define_ir(&mut ir_stack);
|
||||||
|
|
||||||
println!("{ir_stack:#?}");
|
println!("AFTER FUNCTION DEFINITIONS: {ir_stack:#?}");
|
||||||
|
|
||||||
let mut term = self.uplc_code_gen(&mut ir_stack);
|
let mut term = self.uplc_code_gen(&mut ir_stack);
|
||||||
|
|
||||||
|
@ -188,10 +191,7 @@ impl<'a> CodeGenerator<'a> {
|
||||||
if let Some(tail) = tail {
|
if let Some(tail) = tail {
|
||||||
let mut scope = scope;
|
let mut scope = scope;
|
||||||
scope.push(self.id_gen.next());
|
scope.push(self.id_gen.next());
|
||||||
ir_stack.push(IR::Tail {
|
|
||||||
scope: scope.clone(),
|
|
||||||
count: 1,
|
|
||||||
});
|
|
||||||
self.build_ir(tail, ir_stack, scope);
|
self.build_ir(tail, ir_stack, scope);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -267,20 +267,34 @@ impl<'a> CodeGenerator<'a> {
|
||||||
let subject = subjects[0].clone();
|
let subject = subjects[0].clone();
|
||||||
let mut needs_subject_var = false;
|
let mut needs_subject_var = false;
|
||||||
|
|
||||||
|
let clauses = if matches!(clauses[0].pattern[0], Pattern::List { .. }) {
|
||||||
|
rearrange_clauses(clauses.clone())
|
||||||
|
} else {
|
||||||
|
clauses.clone()
|
||||||
|
};
|
||||||
|
|
||||||
if let Some((last_clause, clauses)) = clauses.split_last() {
|
if let Some((last_clause, clauses)) = clauses.split_last() {
|
||||||
let mut clauses_vec = vec![];
|
let mut clauses_vec = vec![];
|
||||||
let mut pattern_vec = vec![];
|
let mut pattern_vec = vec![];
|
||||||
|
|
||||||
for clause in clauses {
|
let mut clause_properties = ClauseProperties {
|
||||||
|
clause_var_name: constr_var.clone(),
|
||||||
|
needs_constr_var: false,
|
||||||
|
is_complex_clause: false,
|
||||||
|
current_index: 0,
|
||||||
|
original_subject_name: subject_name.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
for (index, clause) in clauses.iter().enumerate() {
|
||||||
|
// scope per clause is different
|
||||||
let mut scope = scope.clone();
|
let mut scope = scope.clone();
|
||||||
scope.push(self.id_gen.next());
|
scope.push(self.id_gen.next());
|
||||||
|
|
||||||
|
// holds when clause pattern IR
|
||||||
let mut clause_subject_vec = vec![];
|
let mut clause_subject_vec = vec![];
|
||||||
|
|
||||||
let mut clause_complexity = ClauseComplexity {
|
// reset complex clause setting per clause back to default
|
||||||
subject_var_name: constr_var.clone(),
|
clause_properties.is_complex_clause = false;
|
||||||
needs_subject_var: false,
|
|
||||||
is_complex_clause: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.build_ir(&clause.then, &mut clauses_vec, scope.clone());
|
self.build_ir(&clause.then, &mut clauses_vec, scope.clone());
|
||||||
|
|
||||||
|
@ -289,33 +303,51 @@ impl<'a> CodeGenerator<'a> {
|
||||||
&mut clause_subject_vec,
|
&mut clause_subject_vec,
|
||||||
&mut clauses_vec,
|
&mut clauses_vec,
|
||||||
&subject.tipo(),
|
&subject.tipo(),
|
||||||
&mut clause_complexity,
|
&mut clause_properties,
|
||||||
scope.clone(),
|
scope.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
pattern_vec.push(IR::Clause {
|
if clause_properties.needs_constr_var {
|
||||||
scope: scope.clone(),
|
|
||||||
count: 2,
|
|
||||||
tipo: subject.tipo().clone(),
|
|
||||||
subject_name: subject_name.clone(),
|
|
||||||
complex_clause: clause_complexity.is_complex_clause,
|
|
||||||
});
|
|
||||||
|
|
||||||
pattern_vec.append(&mut clause_subject_vec);
|
|
||||||
|
|
||||||
if clause_complexity.needs_subject_var {
|
|
||||||
needs_subject_var = true;
|
needs_subject_var = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let subject_name = if clause_properties.current_index == 0 {
|
||||||
|
subject_name.clone()
|
||||||
|
} else {
|
||||||
|
format!("__tail_{}", clause_properties.current_index - 1)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Clause is first in IR pattern vec
|
||||||
|
if subject.tipo().is_list() {
|
||||||
|
let next_tail = if index == clauses.len() - 1 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(format!("__tail_{}", clause_properties.current_index))
|
||||||
|
};
|
||||||
|
|
||||||
|
pattern_vec.push(IR::ListClause {
|
||||||
|
scope,
|
||||||
|
tipo: subject.tipo().clone(),
|
||||||
|
tail_name: subject_name,
|
||||||
|
complex_clause: clause_properties.is_complex_clause,
|
||||||
|
next_tail_name: next_tail,
|
||||||
|
});
|
||||||
|
|
||||||
|
clause_properties.current_index += 1;
|
||||||
|
} else {
|
||||||
|
pattern_vec.push(IR::Clause {
|
||||||
|
scope,
|
||||||
|
tipo: subject.tipo().clone(),
|
||||||
|
subject_name,
|
||||||
|
complex_clause: clause_properties.is_complex_clause,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pattern_vec.append(&mut clause_subject_vec);
|
||||||
}
|
}
|
||||||
|
|
||||||
let last_pattern = &last_clause.pattern[0];
|
let last_pattern = &last_clause.pattern[0];
|
||||||
|
|
||||||
let mut final_clause_complexity = ClauseComplexity {
|
|
||||||
subject_var_name: constr_var.clone(),
|
|
||||||
needs_subject_var: false,
|
|
||||||
is_complex_clause: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut final_scope = scope.clone();
|
let mut final_scope = scope.clone();
|
||||||
final_scope.push(self.id_gen.next());
|
final_scope.push(self.id_gen.next());
|
||||||
pattern_vec.push(IR::Finally {
|
pattern_vec.push(IR::Finally {
|
||||||
|
@ -323,16 +355,17 @@ impl<'a> CodeGenerator<'a> {
|
||||||
});
|
});
|
||||||
|
|
||||||
self.build_ir(&last_clause.then, &mut clauses_vec, final_scope.clone());
|
self.build_ir(&last_clause.then, &mut clauses_vec, final_scope.clone());
|
||||||
|
|
||||||
self.when_ir(
|
self.when_ir(
|
||||||
last_pattern,
|
last_pattern,
|
||||||
&mut pattern_vec,
|
&mut pattern_vec,
|
||||||
&mut clauses_vec,
|
&mut clauses_vec,
|
||||||
&subject.tipo(),
|
&subject.tipo(),
|
||||||
&mut final_clause_complexity,
|
&mut clause_properties,
|
||||||
final_scope,
|
final_scope,
|
||||||
);
|
);
|
||||||
|
|
||||||
if needs_subject_var || final_clause_complexity.needs_subject_var {
|
if needs_subject_var || clause_properties.needs_constr_var {
|
||||||
ir_stack.push(IR::Lam {
|
ir_stack.push(IR::Lam {
|
||||||
scope: scope.clone(),
|
scope: scope.clone(),
|
||||||
name: constr_var.clone(),
|
name: constr_var.clone(),
|
||||||
|
@ -342,7 +375,6 @@ impl<'a> CodeGenerator<'a> {
|
||||||
|
|
||||||
ir_stack.push(IR::When {
|
ir_stack.push(IR::When {
|
||||||
scope: scope.clone(),
|
scope: scope.clone(),
|
||||||
count: clauses.len() + 1,
|
|
||||||
subject_name,
|
subject_name,
|
||||||
tipo: subject.tipo(),
|
tipo: subject.tipo(),
|
||||||
});
|
});
|
||||||
|
@ -363,7 +395,6 @@ impl<'a> CodeGenerator<'a> {
|
||||||
} else {
|
} else {
|
||||||
ir_stack.push(IR::When {
|
ir_stack.push(IR::When {
|
||||||
scope: scope.clone(),
|
scope: scope.clone(),
|
||||||
count: clauses.len() + 1,
|
|
||||||
subject_name,
|
subject_name,
|
||||||
tipo: subject.tipo(),
|
tipo: subject.tipo(),
|
||||||
});
|
});
|
||||||
|
@ -391,12 +422,10 @@ impl<'a> CodeGenerator<'a> {
|
||||||
if index == 0 {
|
if index == 0 {
|
||||||
if_ir.push(IR::If {
|
if_ir.push(IR::If {
|
||||||
scope: scope.clone(),
|
scope: scope.clone(),
|
||||||
count: 3,
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
if_ir.push(IR::If {
|
if_ir.push(IR::If {
|
||||||
scope: branch_scope.clone(),
|
scope: branch_scope.clone(),
|
||||||
count: 3,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
self.build_ir(&branch.condition, &mut if_ir, branch_scope.clone());
|
self.build_ir(&branch.condition, &mut if_ir, branch_scope.clone());
|
||||||
|
@ -485,7 +514,7 @@ impl<'a> CodeGenerator<'a> {
|
||||||
pattern_vec: &mut Vec<IR>,
|
pattern_vec: &mut Vec<IR>,
|
||||||
values: &mut Vec<IR>,
|
values: &mut Vec<IR>,
|
||||||
tipo: &Type,
|
tipo: &Type,
|
||||||
when_complexity: &mut ClauseComplexity,
|
clause_properties: &mut ClauseProperties,
|
||||||
scope: Vec<u64>,
|
scope: Vec<u64>,
|
||||||
) {
|
) {
|
||||||
match pattern {
|
match pattern {
|
||||||
|
@ -499,25 +528,90 @@ impl<'a> CodeGenerator<'a> {
|
||||||
}
|
}
|
||||||
Pattern::String { .. } => todo!(),
|
Pattern::String { .. } => todo!(),
|
||||||
Pattern::Var { name, .. } => {
|
Pattern::Var { name, .. } => {
|
||||||
|
pattern_vec.push(IR::Discard {
|
||||||
|
scope: scope.clone(),
|
||||||
|
});
|
||||||
|
pattern_vec.push(IR::Lam {
|
||||||
|
scope: scope.clone(),
|
||||||
|
name: name.clone(),
|
||||||
|
});
|
||||||
|
|
||||||
pattern_vec.push(IR::Var {
|
pattern_vec.push(IR::Var {
|
||||||
scope,
|
scope,
|
||||||
name: name.clone(),
|
|
||||||
constructor: ValueConstructor::public(
|
constructor: ValueConstructor::public(
|
||||||
tipo.clone().into(),
|
tipo.clone().into(),
|
||||||
ValueConstructorVariant::LocalVariable {
|
ValueConstructorVariant::LocalVariable {
|
||||||
location: Span::empty(),
|
location: Span::empty(),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
name: clause_properties.original_subject_name.clone(),
|
||||||
});
|
});
|
||||||
pattern_vec.append(values);
|
pattern_vec.append(values);
|
||||||
}
|
}
|
||||||
Pattern::VarUsage { .. } => todo!(),
|
Pattern::VarUsage { .. } => todo!(),
|
||||||
Pattern::Assign { .. } => todo!(),
|
Pattern::Assign { name, pattern, .. } => {
|
||||||
|
let mut new_vec = vec![];
|
||||||
|
new_vec.push(IR::Lam {
|
||||||
|
scope: scope.clone(),
|
||||||
|
name: name.clone(),
|
||||||
|
});
|
||||||
|
new_vec.push(IR::Var {
|
||||||
|
scope: scope.clone(),
|
||||||
|
constructor: ValueConstructor::public(
|
||||||
|
tipo.clone().into(),
|
||||||
|
ValueConstructorVariant::LocalVariable {
|
||||||
|
location: Span::empty(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
name: clause_properties.original_subject_name.clone(),
|
||||||
|
});
|
||||||
|
|
||||||
|
new_vec.append(values);
|
||||||
|
|
||||||
|
// pattern_vec.push(value)
|
||||||
|
self.when_ir(
|
||||||
|
pattern,
|
||||||
|
pattern_vec,
|
||||||
|
&mut new_vec,
|
||||||
|
tipo,
|
||||||
|
clause_properties,
|
||||||
|
scope,
|
||||||
|
);
|
||||||
|
}
|
||||||
Pattern::Discard { .. } => {
|
Pattern::Discard { .. } => {
|
||||||
pattern_vec.push(IR::Discard { scope });
|
pattern_vec.push(IR::Discard { scope });
|
||||||
pattern_vec.append(values);
|
pattern_vec.append(values);
|
||||||
}
|
}
|
||||||
Pattern::List { .. } => todo!(),
|
Pattern::List { elements, tail, .. } => {
|
||||||
|
let mut needs_clause_guard = false;
|
||||||
|
|
||||||
|
for element in elements {
|
||||||
|
check_when_pattern_needs(element, &mut false, &mut needs_clause_guard);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(tail) = tail {
|
||||||
|
check_when_pattern_needs(tail, &mut false, &mut needs_clause_guard);
|
||||||
|
}
|
||||||
|
|
||||||
|
if needs_clause_guard {
|
||||||
|
clause_properties.is_complex_clause = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pattern_vec.push(IR::Discard {
|
||||||
|
scope: scope.clone(),
|
||||||
|
});
|
||||||
|
|
||||||
|
self.when_recursive_ir(
|
||||||
|
pattern,
|
||||||
|
pattern_vec,
|
||||||
|
&mut vec![],
|
||||||
|
clause_properties.clone(),
|
||||||
|
tipo,
|
||||||
|
scope,
|
||||||
|
);
|
||||||
|
|
||||||
|
pattern_vec.append(values);
|
||||||
|
}
|
||||||
Pattern::Constructor {
|
Pattern::Constructor {
|
||||||
arguments,
|
arguments,
|
||||||
name: constr_name,
|
name: constr_name,
|
||||||
|
@ -527,20 +621,11 @@ impl<'a> CodeGenerator<'a> {
|
||||||
let mut needs_clause_guard = false;
|
let mut needs_clause_guard = false;
|
||||||
|
|
||||||
for arg in arguments {
|
for arg in arguments {
|
||||||
match arg.value {
|
check_when_pattern_needs(
|
||||||
Pattern::Var { .. } => {
|
&arg.value,
|
||||||
needs_access_to_constr_var = true;
|
&mut needs_access_to_constr_var,
|
||||||
}
|
&mut needs_clause_guard,
|
||||||
Pattern::List { .. }
|
);
|
||||||
| Pattern::Constructor { .. }
|
|
||||||
| Pattern::Tuple { .. } => {
|
|
||||||
needs_access_to_constr_var = true;
|
|
||||||
needs_clause_guard = true;
|
|
||||||
}
|
|
||||||
Pattern::Discard { .. } => {}
|
|
||||||
|
|
||||||
_ => todo!("{arg:#?}"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let data_type_key = match tipo {
|
let data_type_key = match tipo {
|
||||||
|
@ -574,7 +659,7 @@ impl<'a> CodeGenerator<'a> {
|
||||||
location: Span::empty(),
|
location: Span::empty(),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
name: when_complexity.subject_var_name.clone(),
|
name: clause_properties.clause_var_name.clone(),
|
||||||
scope: scope.clone(),
|
scope: scope.clone(),
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
@ -588,16 +673,30 @@ impl<'a> CodeGenerator<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if needs_clause_guard {
|
if needs_clause_guard {
|
||||||
when_complexity.is_complex_clause = true;
|
clause_properties.is_complex_clause = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if needs_access_to_constr_var {
|
if needs_access_to_constr_var {
|
||||||
when_complexity.needs_subject_var = true;
|
clause_properties.needs_constr_var = true;
|
||||||
|
|
||||||
self.when_recursive_ir(pattern, pattern_vec, &mut new_vec, tipo, scope);
|
self.when_recursive_ir(
|
||||||
|
pattern,
|
||||||
|
pattern_vec,
|
||||||
|
&mut new_vec,
|
||||||
|
clause_properties.clone(),
|
||||||
|
tipo,
|
||||||
|
scope,
|
||||||
|
);
|
||||||
pattern_vec.append(values);
|
pattern_vec.append(values);
|
||||||
} else {
|
} else {
|
||||||
self.when_recursive_ir(pattern, pattern_vec, &mut vec![], tipo, scope);
|
self.when_recursive_ir(
|
||||||
|
pattern,
|
||||||
|
pattern_vec,
|
||||||
|
&mut vec![],
|
||||||
|
clause_properties.clone(),
|
||||||
|
tipo,
|
||||||
|
scope,
|
||||||
|
);
|
||||||
pattern_vec.append(values);
|
pattern_vec.append(values);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -610,6 +709,7 @@ impl<'a> CodeGenerator<'a> {
|
||||||
pattern: &Pattern<tipo::PatternConstructor, Arc<tipo::Type>>,
|
pattern: &Pattern<tipo::PatternConstructor, Arc<tipo::Type>>,
|
||||||
pattern_vec: &mut Vec<IR>,
|
pattern_vec: &mut Vec<IR>,
|
||||||
values: &mut Vec<IR>,
|
values: &mut Vec<IR>,
|
||||||
|
clause_properties: ClauseProperties,
|
||||||
_tipo: &Type,
|
_tipo: &Type,
|
||||||
scope: Vec<u64>,
|
scope: Vec<u64>,
|
||||||
) {
|
) {
|
||||||
|
@ -625,7 +725,7 @@ impl<'a> CodeGenerator<'a> {
|
||||||
pattern_vec.append(values);
|
pattern_vec.append(values);
|
||||||
}
|
}
|
||||||
Pattern::List { elements, tail, .. } => {
|
Pattern::List { elements, tail, .. } => {
|
||||||
let mut elements_vec = vec![];
|
// let mut elements_vec = vec![];
|
||||||
|
|
||||||
let mut names = vec![];
|
let mut names = vec![];
|
||||||
for element in elements {
|
for element in elements {
|
||||||
|
@ -633,49 +733,85 @@ impl<'a> CodeGenerator<'a> {
|
||||||
Pattern::Var { name, .. } => {
|
Pattern::Var { name, .. } => {
|
||||||
names.push(name.clone());
|
names.push(name.clone());
|
||||||
}
|
}
|
||||||
a @ Pattern::List { .. } => {
|
Pattern::Discard { .. } => {
|
||||||
let mut var_vec = vec![];
|
names.push("_".to_string());
|
||||||
let item_name = format!("list_item_id_{}", self.id_gen.next());
|
|
||||||
names.push(item_name.clone());
|
|
||||||
var_vec.push(IR::Var {
|
|
||||||
constructor: ValueConstructor::public(
|
|
||||||
Type::App {
|
|
||||||
public: true,
|
|
||||||
module: String::new(),
|
|
||||||
name: String::new(),
|
|
||||||
args: vec![],
|
|
||||||
}
|
}
|
||||||
.into(),
|
Pattern::List { .. } => {
|
||||||
ValueConstructorVariant::LocalVariable {
|
todo!("Nested List Patterns Not Yet Done");
|
||||||
location: Span::empty(),
|
// let mut var_vec = vec![];
|
||||||
},
|
// let item_name = format!("list_item_id_{}", self.id_gen.next());
|
||||||
),
|
// names.push(item_name.clone());
|
||||||
name: item_name,
|
// var_vec.push(IR::Var {
|
||||||
scope: scope.clone(),
|
// constructor: ValueConstructor::public(
|
||||||
});
|
// Type::App {
|
||||||
self.pattern_ir(a, &mut elements_vec, &mut var_vec, scope.clone());
|
// public: true,
|
||||||
|
// module: String::new(),
|
||||||
|
// name: String::new(),
|
||||||
|
// args: vec![],
|
||||||
|
// }
|
||||||
|
// .into(),
|
||||||
|
// ValueConstructorVariant::LocalVariable {
|
||||||
|
// location: Span::empty(),
|
||||||
|
// },
|
||||||
|
// ),
|
||||||
|
// name: item_name,
|
||||||
|
// scope: scope.clone(),
|
||||||
|
// });
|
||||||
|
// self.pattern_ir(a, &mut elements_vec, &mut var_vec, scope.clone());
|
||||||
}
|
}
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut tail_name = String::new();
|
||||||
|
|
||||||
if let Some(tail) = tail {
|
if let Some(tail) = tail {
|
||||||
match &**tail {
|
match &**tail {
|
||||||
Pattern::Var { name, .. } => names.push(name.clone()),
|
Pattern::Var { name, .. } => {
|
||||||
|
tail_name = name.clone();
|
||||||
|
}
|
||||||
Pattern::Discard { .. } => {}
|
Pattern::Discard { .. } => {}
|
||||||
_ => unreachable!(),
|
_ => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pattern_vec.push(IR::ListAccessor {
|
let tail_head_names = names
|
||||||
names,
|
.iter()
|
||||||
tail: tail.is_some(),
|
.enumerate()
|
||||||
|
.filter(|(_, name)| *name != &"_".to_string())
|
||||||
|
.map(|(index, name)| {
|
||||||
|
if index == 0 {
|
||||||
|
(
|
||||||
|
clause_properties.original_subject_name.clone(),
|
||||||
|
name.clone(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(format!("__tail_{}", index - 1), name.clone())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect_vec();
|
||||||
|
|
||||||
|
if tail.is_some() && !elements.is_empty() {
|
||||||
|
let tail_var = if elements.len() == 1 {
|
||||||
|
clause_properties.original_subject_name
|
||||||
|
} else {
|
||||||
|
format!("__tail_{}", elements.len() - 2)
|
||||||
|
};
|
||||||
|
|
||||||
|
pattern_vec.push(IR::ListExpose {
|
||||||
scope,
|
scope,
|
||||||
|
tail_head_names,
|
||||||
|
tail: Some((tail_var, tail_name)),
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
pattern_vec.push(IR::ListExpose {
|
||||||
|
scope,
|
||||||
|
tail_head_names,
|
||||||
|
tail: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pattern_vec.append(values);
|
pattern_vec.append(values);
|
||||||
pattern_vec.append(&mut elements_vec);
|
|
||||||
todo!();
|
|
||||||
}
|
}
|
||||||
Pattern::Constructor {
|
Pattern::Constructor {
|
||||||
is_record,
|
is_record,
|
||||||
|
@ -765,10 +901,12 @@ impl<'a> CodeGenerator<'a> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut clause_complexity = ClauseComplexity {
|
let mut clause_complexity = ClauseProperties {
|
||||||
subject_var_name: constr_var_name.clone(),
|
clause_var_name: constr_var_name.clone(),
|
||||||
needs_subject_var: false,
|
needs_constr_var: false,
|
||||||
is_complex_clause: false,
|
is_complex_clause: false,
|
||||||
|
current_index: 0,
|
||||||
|
original_subject_name: constr_var_name.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.when_ir(
|
self.when_ir(
|
||||||
|
@ -852,10 +990,12 @@ impl<'a> CodeGenerator<'a> {
|
||||||
subject_name: constr_var_name.clone(),
|
subject_name: constr_var_name.clone(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
let mut clause_complexity = ClauseComplexity {
|
let mut clause_complexity = ClauseProperties {
|
||||||
subject_var_name: constr_var_name.clone(),
|
clause_var_name: constr_var_name.clone(),
|
||||||
needs_subject_var: false,
|
needs_constr_var: false,
|
||||||
is_complex_clause: false,
|
is_complex_clause: false,
|
||||||
|
current_index: 0,
|
||||||
|
original_subject_name: constr_var_name.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.when_ir(
|
self.when_ir(
|
||||||
|
@ -1386,6 +1526,58 @@ impl<'a> CodeGenerator<'a> {
|
||||||
|
|
||||||
arg_stack.push(term);
|
arg_stack.push(term);
|
||||||
}
|
}
|
||||||
|
IR::ListExpose {
|
||||||
|
tail_head_names,
|
||||||
|
tail,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let mut term = arg_stack.pop().unwrap();
|
||||||
|
|
||||||
|
if let Some((tail_var, tail_name)) = tail {
|
||||||
|
term = Term::Apply {
|
||||||
|
function: Term::Lambda {
|
||||||
|
parameter_name: Name {
|
||||||
|
text: tail_name,
|
||||||
|
unique: 0.into(),
|
||||||
|
},
|
||||||
|
body: term.into(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
argument: Term::Apply {
|
||||||
|
function: Term::Builtin(DefaultFunction::TailList).force_wrap().into(),
|
||||||
|
argument: Term::Var(Name {
|
||||||
|
text: tail_var,
|
||||||
|
unique: 0.into(),
|
||||||
|
})
|
||||||
|
.into(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
for (tail_var, head_name) in tail_head_names.into_iter().rev() {
|
||||||
|
term = Term::Apply {
|
||||||
|
function: Term::Lambda {
|
||||||
|
parameter_name: Name {
|
||||||
|
text: head_name,
|
||||||
|
unique: 0.into(),
|
||||||
|
},
|
||||||
|
body: term.into(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
argument: Term::Apply {
|
||||||
|
function: Term::Builtin(DefaultFunction::HeadList).force_wrap().into(),
|
||||||
|
argument: Term::Var(Name {
|
||||||
|
text: tail_var,
|
||||||
|
unique: 0.into(),
|
||||||
|
})
|
||||||
|
.into(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
arg_stack.push(term);
|
||||||
|
}
|
||||||
IR::Call { count, .. } => {
|
IR::Call { count, .. } => {
|
||||||
if count >= 2 {
|
if count >= 2 {
|
||||||
let mut term = arg_stack.pop().unwrap();
|
let mut term = arg_stack.pop().unwrap();
|
||||||
|
@ -1762,7 +1954,7 @@ impl<'a> CodeGenerator<'a> {
|
||||||
// the body to be run if the clause matches
|
// the body to be run if the clause matches
|
||||||
let body = arg_stack.pop().unwrap();
|
let body = arg_stack.pop().unwrap();
|
||||||
|
|
||||||
// the final branch in the when expression
|
// the next branch in the when expression
|
||||||
let mut term = arg_stack.pop().unwrap();
|
let mut term = arg_stack.pop().unwrap();
|
||||||
|
|
||||||
let checker = if tipo.is_int() {
|
let checker = if tipo.is_int() {
|
||||||
|
@ -1795,7 +1987,7 @@ impl<'a> CodeGenerator<'a> {
|
||||||
.into(),
|
.into(),
|
||||||
}
|
}
|
||||||
} else if tipo.is_list() {
|
} else if tipo.is_list() {
|
||||||
todo!()
|
unreachable!()
|
||||||
} else {
|
} else {
|
||||||
Term::Apply {
|
Term::Apply {
|
||||||
function: DefaultFunction::EqualsInteger.into(),
|
function: DefaultFunction::EqualsInteger.into(),
|
||||||
|
@ -1864,6 +2056,68 @@ impl<'a> CodeGenerator<'a> {
|
||||||
|
|
||||||
arg_stack.push(term);
|
arg_stack.push(term);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IR::ListClause {
|
||||||
|
tail_name,
|
||||||
|
next_tail_name,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
// discard to pop off
|
||||||
|
let _ = arg_stack.pop().unwrap();
|
||||||
|
|
||||||
|
// the body to be run if the clause matches
|
||||||
|
let body = arg_stack.pop().unwrap();
|
||||||
|
|
||||||
|
// the next branch in the when expression
|
||||||
|
let mut term = arg_stack.pop().unwrap();
|
||||||
|
|
||||||
|
let arg = if let Some(next_tail_name) = next_tail_name {
|
||||||
|
Term::Apply {
|
||||||
|
function: Term::Lambda {
|
||||||
|
parameter_name: Name {
|
||||||
|
text: next_tail_name,
|
||||||
|
unique: 0.into(),
|
||||||
|
},
|
||||||
|
body: term.into(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
argument: Term::Apply {
|
||||||
|
function: Term::Builtin(DefaultFunction::TailList).force_wrap().into(),
|
||||||
|
argument: Term::Var(Name {
|
||||||
|
text: tail_name.clone(),
|
||||||
|
unique: 0.into(),
|
||||||
|
})
|
||||||
|
.into(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
term
|
||||||
|
};
|
||||||
|
|
||||||
|
term = Term::Apply {
|
||||||
|
function: Term::Apply {
|
||||||
|
function: Term::Apply {
|
||||||
|
function: Term::Builtin(DefaultFunction::ChooseList)
|
||||||
|
.force_wrap()
|
||||||
|
.force_wrap()
|
||||||
|
.into(),
|
||||||
|
argument: Term::Var(Name {
|
||||||
|
text: tail_name,
|
||||||
|
unique: 0.into(),
|
||||||
|
})
|
||||||
|
.into(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
argument: Term::Delay(body.into()).into(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
argument: Term::Delay(arg.into()).into(),
|
||||||
|
}
|
||||||
|
.force_wrap();
|
||||||
|
|
||||||
|
arg_stack.push(term);
|
||||||
|
}
|
||||||
IR::ClauseGuard {
|
IR::ClauseGuard {
|
||||||
subject_name, tipo, ..
|
subject_name, tipo, ..
|
||||||
} => {
|
} => {
|
||||||
|
@ -2562,6 +2816,28 @@ impl<'a> CodeGenerator<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_when_pattern_needs(
|
||||||
|
pattern: &Pattern<tipo::PatternConstructor, Arc<Type>>,
|
||||||
|
needs_access_to_constr_var: &mut bool,
|
||||||
|
needs_clause_guard: &mut bool,
|
||||||
|
) {
|
||||||
|
match pattern {
|
||||||
|
Pattern::Var { .. } => {
|
||||||
|
*needs_access_to_constr_var = true;
|
||||||
|
}
|
||||||
|
Pattern::List { .. }
|
||||||
|
| Pattern::Constructor { .. }
|
||||||
|
| Pattern::Tuple { .. }
|
||||||
|
| Pattern::Int { .. } => {
|
||||||
|
*needs_access_to_constr_var = true;
|
||||||
|
*needs_clause_guard = true;
|
||||||
|
}
|
||||||
|
Pattern::Discard { .. } => {}
|
||||||
|
|
||||||
|
_ => todo!("{pattern:#?}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn get_common_ancestor(scope: &[u64], scope_prev: &[u64]) -> Vec<u64> {
|
fn get_common_ancestor(scope: &[u64], scope_prev: &[u64]) -> Vec<u64> {
|
||||||
let longest_length = if scope.len() >= scope_prev.len() {
|
let longest_length = if scope.len() >= scope_prev.len() {
|
||||||
scope.len()
|
scope.len()
|
||||||
|
@ -2723,3 +2999,149 @@ fn list_access_to_uplc(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn rearrange_clauses(
|
||||||
|
clauses: Vec<Clause<TypedExpr, PatternConstructor, Arc<Type>, String>>,
|
||||||
|
) -> Vec<Clause<TypedExpr, PatternConstructor, Arc<Type>, String>> {
|
||||||
|
let mut sorted_clauses = clauses;
|
||||||
|
|
||||||
|
// if we have a list sort clauses so we can plug holes for cases not covered by clauses
|
||||||
|
sorted_clauses.sort_by(|clause1, clause2| {
|
||||||
|
let clause1_len = match &clause1.pattern[0] {
|
||||||
|
Pattern::List { elements, tail, .. } => elements.len() + usize::from(tail.is_some()),
|
||||||
|
_ => 1000000,
|
||||||
|
};
|
||||||
|
let clause2_len = match &clause2.pattern[0] {
|
||||||
|
Pattern::List { elements, tail, .. } => elements.len() + usize::from(tail.is_some()),
|
||||||
|
_ => 1000001,
|
||||||
|
};
|
||||||
|
|
||||||
|
clause1_len.cmp(&clause2_len)
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut elems_len = 0;
|
||||||
|
let mut final_clauses = sorted_clauses.clone();
|
||||||
|
let mut holes_to_fill = vec![];
|
||||||
|
let mut assign_plug_in_name = None;
|
||||||
|
let mut last_clause_index = 0;
|
||||||
|
let mut last_clause_set = false;
|
||||||
|
|
||||||
|
// If we have a catch all, use that. Otherwise use todo which will result in error
|
||||||
|
// TODO: fill in todo label with description
|
||||||
|
let plug_in_then = match &sorted_clauses[sorted_clauses.len() - 1].pattern[0] {
|
||||||
|
Pattern::Var { name, .. } => {
|
||||||
|
assign_plug_in_name = Some(name);
|
||||||
|
sorted_clauses[sorted_clauses.len() - 1].clone().then
|
||||||
|
}
|
||||||
|
Pattern::Discard { .. } => sorted_clauses[sorted_clauses.len() - 1].clone().then,
|
||||||
|
_ => TypedExpr::Todo {
|
||||||
|
location: Span::empty(),
|
||||||
|
label: None,
|
||||||
|
tipo: sorted_clauses[sorted_clauses.len() - 1].then.tipo(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (index, clause) in sorted_clauses.iter().enumerate() {
|
||||||
|
if let Pattern::List { elements, .. } = &clause.pattern[0] {
|
||||||
|
while elems_len < elements.len() {
|
||||||
|
let mut discard_elems = vec![];
|
||||||
|
|
||||||
|
for _ in 0..elems_len {
|
||||||
|
discard_elems.push(Pattern::Discard {
|
||||||
|
name: "_".to_string(),
|
||||||
|
location: Span::empty(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let clause_to_fill = if let Some(name) = assign_plug_in_name {
|
||||||
|
Clause {
|
||||||
|
location: Span::empty(),
|
||||||
|
pattern: vec![Pattern::Assign {
|
||||||
|
name: name.clone(),
|
||||||
|
location: Span::empty(),
|
||||||
|
pattern: Pattern::List {
|
||||||
|
location: Span::empty(),
|
||||||
|
elements: discard_elems,
|
||||||
|
tail: None,
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
}],
|
||||||
|
alternative_patterns: vec![],
|
||||||
|
guard: None,
|
||||||
|
then: plug_in_then.clone(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Clause {
|
||||||
|
location: Span::empty(),
|
||||||
|
pattern: vec![Pattern::List {
|
||||||
|
location: Span::empty(),
|
||||||
|
elements: discard_elems,
|
||||||
|
tail: None,
|
||||||
|
}],
|
||||||
|
alternative_patterns: vec![],
|
||||||
|
guard: None,
|
||||||
|
then: plug_in_then.clone(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
holes_to_fill.push((index, clause_to_fill));
|
||||||
|
elems_len += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Pattern::List {
|
||||||
|
elements,
|
||||||
|
tail: Some(tail),
|
||||||
|
..
|
||||||
|
} = &clause.pattern[0]
|
||||||
|
{
|
||||||
|
let mut elements = elements.clone();
|
||||||
|
elements.push(*tail.clone());
|
||||||
|
if elements
|
||||||
|
.iter()
|
||||||
|
.all(|element| matches!(element, Pattern::Var { .. } | Pattern::Discard { .. }))
|
||||||
|
&& !last_clause_set
|
||||||
|
{
|
||||||
|
last_clause_index = index;
|
||||||
|
last_clause_set = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if index == sorted_clauses.len() - 1 {
|
||||||
|
if let Pattern::List {
|
||||||
|
elements,
|
||||||
|
tail: Some(tail),
|
||||||
|
..
|
||||||
|
} = &clause.pattern[0]
|
||||||
|
{
|
||||||
|
let mut elements = elements.clone();
|
||||||
|
elements.push(*tail.clone());
|
||||||
|
if !elements
|
||||||
|
.iter()
|
||||||
|
.all(|element| matches!(element, Pattern::Var { .. } | Pattern::Discard { .. }))
|
||||||
|
{
|
||||||
|
final_clauses.push(Clause {
|
||||||
|
location: Span::empty(),
|
||||||
|
pattern: vec![Pattern::Discard {
|
||||||
|
name: "_".to_string(),
|
||||||
|
location: Span::empty(),
|
||||||
|
}],
|
||||||
|
alternative_patterns: vec![],
|
||||||
|
guard: None,
|
||||||
|
then: plug_in_then.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
elems_len += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
final_clauses = final_clauses[0..(last_clause_index + 1)].to_vec();
|
||||||
|
|
||||||
|
for (index, clause) in holes_to_fill.into_iter().rev() {
|
||||||
|
final_clauses.insert(index, clause);
|
||||||
|
}
|
||||||
|
|
||||||
|
final_clauses
|
||||||
|
}
|
||||||
|
|
|
@ -42,10 +42,6 @@ pub fn incrementor(counter: Int, target: Int) -> Int {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn who(a: ByteArray) -> ByteArray {
|
|
||||||
builtin.append_bytearray(a, #[12, 255])
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Datum {
|
pub type Datum {
|
||||||
Offer { prices: List(Int), asset_class: ByteArray, other_thing: Datum }
|
Offer { prices: List(Int), asset_class: ByteArray, other_thing: Datum }
|
||||||
Sell
|
Sell
|
||||||
|
@ -63,10 +59,17 @@ pub fn spend(datum: Datum, _rdmr: Nil, _ctx: Nil) -> Bool {
|
||||||
..
|
..
|
||||||
},
|
},
|
||||||
} -> 1 == 1
|
} -> 1 == 1
|
||||||
|
Offer { prices, other_thing: thing, .. } ->
|
||||||
|
when prices is {
|
||||||
|
[] -> True
|
||||||
|
[a] -> True
|
||||||
|
[a, b, c] -> True
|
||||||
|
[a, b, c, d, ..e] -> 1 == 1
|
||||||
|
rest -> False
|
||||||
|
}
|
||||||
_ -> False
|
_ -> False
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// let Redeemer{ signer, amount: amount2, other_thing: Redeemer{ signer: nested_signer, ..}} = datum
|
// let Redeemer{ signer, amount: amount2, other_thing: Redeemer{ signer: nested_signer, ..}} = datum
|
||||||
// True
|
// True
|
||||||
// when datum is {
|
// when datum is {
|
||||||
|
@ -78,6 +81,6 @@ pub fn spend(datum: Datum, _rdmr: Nil, _ctx: Nil) -> Bool {
|
||||||
// [] -> True
|
// [] -> True
|
||||||
// [1] -> False
|
// [1] -> False
|
||||||
// [a, b] -> True
|
// [a, b] -> True
|
||||||
// [a, b, ..c] -> False
|
// [a, b, ..[a]] -> False
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
Loading…
Reference in New Issue