parent
0060d29265
commit
32d34d5fd3
|
@ -1,13 +1,11 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use indexmap::IndexSet;
|
||||
use uplc::builtins::DefaultFunction;
|
||||
|
||||
use crate::{
|
||||
ast::{BinOp, UnOp},
|
||||
tipo::{Type, ValueConstructor},
|
||||
IdGenerator,
|
||||
};
|
||||
|
||||
use indexmap::IndexSet;
|
||||
use std::sync::Arc;
|
||||
use uplc::builtins::DefaultFunction;
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Air {
|
||||
// Primitives
|
||||
|
@ -15,53 +13,44 @@ pub enum Air {
|
|||
scope: Vec<u64>,
|
||||
value: String,
|
||||
},
|
||||
|
||||
String {
|
||||
scope: Vec<u64>,
|
||||
value: String,
|
||||
},
|
||||
|
||||
ByteArray {
|
||||
scope: Vec<u64>,
|
||||
bytes: Vec<u8>,
|
||||
},
|
||||
|
||||
Bool {
|
||||
scope: Vec<u64>,
|
||||
value: bool,
|
||||
},
|
||||
|
||||
List {
|
||||
scope: Vec<u64>,
|
||||
count: usize,
|
||||
tipo: Arc<Type>,
|
||||
tail: bool,
|
||||
},
|
||||
|
||||
Tuple {
|
||||
scope: Vec<u64>,
|
||||
tipo: Arc<Type>,
|
||||
count: usize,
|
||||
},
|
||||
|
||||
Void {
|
||||
scope: Vec<u64>,
|
||||
},
|
||||
|
||||
Var {
|
||||
scope: Vec<u64>,
|
||||
constructor: ValueConstructor,
|
||||
name: String,
|
||||
variant_name: String,
|
||||
},
|
||||
|
||||
// Functions
|
||||
Call {
|
||||
scope: Vec<u64>,
|
||||
count: usize,
|
||||
tipo: Arc<Type>,
|
||||
},
|
||||
|
||||
DefineFunc {
|
||||
scope: Vec<u64>,
|
||||
func_name: String,
|
||||
|
@ -70,19 +59,16 @@ pub enum Air {
|
|||
recursive: bool,
|
||||
variant_name: String,
|
||||
},
|
||||
|
||||
Fn {
|
||||
scope: Vec<u64>,
|
||||
params: Vec<String>,
|
||||
},
|
||||
|
||||
Builtin {
|
||||
scope: Vec<u64>,
|
||||
count: usize,
|
||||
func: DefaultFunction,
|
||||
tipo: Arc<Type>,
|
||||
},
|
||||
|
||||
// Operators
|
||||
BinOp {
|
||||
scope: Vec<u64>,
|
||||
|
@ -90,52 +76,43 @@ pub enum Air {
|
|||
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>,
|
||||
},
|
||||
|
||||
WrapData {
|
||||
scope: Vec<u64>,
|
||||
tipo: Arc<Type>,
|
||||
},
|
||||
|
||||
AssertConstr {
|
||||
scope: Vec<u64>,
|
||||
constr_index: usize,
|
||||
},
|
||||
|
||||
AssertBool {
|
||||
scope: Vec<u64>,
|
||||
is_true: bool,
|
||||
},
|
||||
|
||||
// When
|
||||
When {
|
||||
scope: Vec<u64>,
|
||||
tipo: Arc<Type>,
|
||||
subject_name: String,
|
||||
},
|
||||
|
||||
Clause {
|
||||
scope: Vec<u64>,
|
||||
tipo: Arc<Type>,
|
||||
subject_name: String,
|
||||
complex_clause: bool,
|
||||
},
|
||||
|
||||
ListClause {
|
||||
scope: Vec<u64>,
|
||||
tipo: Arc<Type>,
|
||||
|
@ -143,11 +120,9 @@ pub enum Air {
|
|||
next_tail_name: Option<String>,
|
||||
complex_clause: bool,
|
||||
},
|
||||
|
||||
WrapClause {
|
||||
scope: Vec<u64>,
|
||||
},
|
||||
|
||||
TupleClause {
|
||||
scope: Vec<u64>,
|
||||
tipo: Arc<Type>,
|
||||
|
@ -157,13 +132,11 @@ pub enum Air {
|
|||
count: usize,
|
||||
complex_clause: bool,
|
||||
},
|
||||
|
||||
ClauseGuard {
|
||||
scope: Vec<u64>,
|
||||
subject_name: String,
|
||||
tipo: Arc<Type>,
|
||||
},
|
||||
|
||||
ListClauseGuard {
|
||||
scope: Vec<u64>,
|
||||
tipo: Arc<Type>,
|
||||
|
@ -171,17 +144,14 @@ pub enum Air {
|
|||
next_tail_name: Option<String>,
|
||||
inverse: bool,
|
||||
},
|
||||
|
||||
Finally {
|
||||
scope: Vec<u64>,
|
||||
},
|
||||
|
||||
// If
|
||||
If {
|
||||
scope: Vec<u64>,
|
||||
tipo: Arc<Type>,
|
||||
},
|
||||
|
||||
// Record Creation
|
||||
Record {
|
||||
scope: Vec<u64>,
|
||||
|
@ -189,27 +159,23 @@ pub enum Air {
|
|||
tipo: Arc<Type>,
|
||||
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,
|
||||
tipo: Arc<Type>,
|
||||
},
|
||||
|
||||
FieldsExpose {
|
||||
scope: Vec<u64>,
|
||||
indices: Vec<(usize, String, Arc<Type>)>,
|
||||
check_last_item: bool,
|
||||
},
|
||||
|
||||
// ListAccess
|
||||
ListAccessor {
|
||||
scope: Vec<u64>,
|
||||
|
@ -218,14 +184,12 @@ pub enum Air {
|
|||
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>,
|
||||
|
@ -233,19 +197,16 @@ pub enum Air {
|
|||
tipo: Arc<Type>,
|
||||
check_last_item: bool,
|
||||
},
|
||||
|
||||
TupleIndex {
|
||||
scope: Vec<u64>,
|
||||
tipo: Arc<Type>,
|
||||
tuple_index: usize,
|
||||
},
|
||||
|
||||
// Misc.
|
||||
ErrorTerm {
|
||||
scope: Vec<u64>,
|
||||
tipo: Arc<Type>,
|
||||
},
|
||||
|
||||
Trace {
|
||||
scope: Vec<u64>,
|
||||
tipo: Arc<Type>,
|
||||
|
@ -295,7 +256,6 @@ impl Air {
|
|||
| Air::Trace { scope, .. } => scope.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scope_mut(&mut self) -> &mut Vec<u64> {
|
||||
match self {
|
||||
Air::Int { scope, .. }
|
||||
|
@ -338,7 +298,6 @@ impl Air {
|
|||
| Air::Trace { scope, .. } => scope,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tipo(&self) -> Option<Arc<Type>> {
|
||||
match self {
|
||||
Air::Int { .. } => Some(
|
||||
|
@ -418,7 +377,6 @@ impl Air {
|
|||
| Air::TupleIndex { tipo, .. }
|
||||
| Air::ErrorTerm { tipo, .. }
|
||||
| Air::Trace { tipo, .. } => Some(tipo.clone()),
|
||||
|
||||
Air::DefineFunc { .. }
|
||||
| Air::Fn { .. }
|
||||
| Air::Let { .. }
|
||||
|
@ -427,7 +385,6 @@ impl Air {
|
|||
| Air::AssertBool { .. }
|
||||
| Air::Finally { .. }
|
||||
| Air::FieldsExpose { .. } => None,
|
||||
|
||||
Air::UnOp { op, .. } => match op {
|
||||
UnOp::Not => Some(
|
||||
Type::App {
|
||||
|
@ -451,3 +408,55 @@ impl Air {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AirEnv<'a> {
|
||||
pub id_gen: &'a mut IdGenerator,
|
||||
pub scope: Vec<u64>,
|
||||
pub air: Vec<Air>,
|
||||
}
|
||||
|
||||
impl<'a> AirEnv<'a> {
|
||||
pub fn new(id_gen: &'a mut IdGenerator) -> Self {
|
||||
AirEnv {
|
||||
id_gen,
|
||||
scope: vec![0],
|
||||
air: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_scope(id_gen: &'a mut IdGenerator, scope: Vec<u64>) -> Self {
|
||||
AirEnv {
|
||||
id_gen,
|
||||
scope,
|
||||
air: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn int(mut self, value: String) -> Self {
|
||||
self.air.push(Air::Int {
|
||||
scope: self.scope.clone(),
|
||||
value,
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
pub fn string(mut self, value: String) -> Self {
|
||||
self.air.push(Air::String {
|
||||
scope: self.scope.clone(),
|
||||
value,
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
pub fn byte_array(mut self, bytes: Vec<u8>) -> Self {
|
||||
self.air.push(Air::ByteArray {
|
||||
scope: self.scope.clone(),
|
||||
bytes,
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
// pub fn sequence(mut self, expressions: AirEnv) -> Self{
|
||||
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -208,14 +208,14 @@ pub fn convert_type_to_data(term: Term<Name>, field_type: &Arc<Type>) -> Term<Na
|
|||
Term::list_data()
|
||||
.apply(
|
||||
Term::mk_cons()
|
||||
.apply(Term::fst_pair().apply(Term::var("__pair".to_string())))
|
||||
.apply(Term::fst_pair().apply(Term::var("__pair")))
|
||||
.apply(
|
||||
Term::mk_cons()
|
||||
.apply(Term::snd_pair().apply(Term::var("__pair".to_string())))
|
||||
.apply(Term::snd_pair().apply(Term::var("__pair")))
|
||||
.apply(Term::empty_list()),
|
||||
),
|
||||
)
|
||||
.lambda("__pair".to_string())
|
||||
.lambda("__pair")
|
||||
.apply(term)
|
||||
} else if field_type.is_list() || field_type.is_tuple() {
|
||||
Term::list_data().apply(term)
|
||||
|
@ -260,8 +260,8 @@ pub fn convert_data_to_type(term: Term<Name>, field_type: &Arc<Type>) -> Term<Na
|
|||
} else if field_type.is_tuple() && matches!(field_type.get_uplc_type(), UplcType::Pair(_, _)) {
|
||||
Term::mk_pair_data()
|
||||
.apply(Term::head_list().apply(Term::var("__list_data")))
|
||||
.apply(Term::head_list().apply(Term::var("__tail".to_string())))
|
||||
.lambda("__tail".to_string())
|
||||
.apply(Term::head_list().apply(Term::var("__tail")))
|
||||
.lambda("__tail")
|
||||
.apply(Term::tail_list().apply(Term::var("__list_data")))
|
||||
.lambda("__list_data")
|
||||
.apply(Term::unlist_data().apply(term))
|
||||
|
@ -470,7 +470,7 @@ pub fn list_access_to_uplc(
|
|||
|
||||
if names.len() == 1 && tail {
|
||||
if first == "_" && names[0] == "_" {
|
||||
term.lambda("_".to_string())
|
||||
term.lambda("_")
|
||||
} else if first == "_" {
|
||||
term.lambda(names[0].clone())
|
||||
.apply(Term::tail_list().apply(Term::var(format!(
|
||||
|
@ -580,7 +580,7 @@ pub fn list_access_to_uplc(
|
|||
|
||||
match &list_access_inner {
|
||||
Term::Lambda { .. } => list_access_inner,
|
||||
_ => list_access_inner.lambda("_".to_string()),
|
||||
_ => list_access_inner.lambda("_"),
|
||||
}
|
||||
} else {
|
||||
let mut list_access_inner = list_access_to_uplc(
|
||||
|
@ -978,14 +978,14 @@ pub fn wrap_validator_args(term: Term<Name>, arguments: &[TypedArg]) -> Term<Nam
|
|||
for arg in arguments.iter().rev() {
|
||||
if !matches!(arg.tipo.get_uplc_type(), UplcType::Data) {
|
||||
term = term
|
||||
.lambda(arg.arg_name.get_variable_name().unwrap_or("_").to_string())
|
||||
.lambda(arg.arg_name.get_variable_name().unwrap_or("_"))
|
||||
.apply(convert_data_to_type(
|
||||
Term::var(arg.arg_name.get_variable_name().unwrap_or("_").to_string()),
|
||||
Term::var(arg.arg_name.get_variable_name().unwrap_or("_")),
|
||||
&arg.tipo,
|
||||
));
|
||||
}
|
||||
|
||||
term = term.lambda(arg.arg_name.get_variable_name().unwrap_or("_").to_string())
|
||||
term = term.lambda(arg.arg_name.get_variable_name().unwrap_or("_"))
|
||||
}
|
||||
term
|
||||
}
|
||||
|
|
|
@ -4615,7 +4615,7 @@ impl<'a> CodeGenerator<'a> {
|
|||
|
||||
term = Term::equals_integer()
|
||||
.apply(Term::integer(constr_index.into()))
|
||||
.apply(Term::var(CONSTR_INDEX_EXPOSER.to_string()).apply(constr))
|
||||
.apply(Term::var(CONSTR_INDEX_EXPOSER).apply(constr))
|
||||
.delayed_if_else(term, error_term);
|
||||
|
||||
arg_stack.push(term);
|
||||
|
@ -4675,7 +4675,7 @@ impl<'a> CodeGenerator<'a> {
|
|||
|
||||
if tipo.is_bool() {
|
||||
let other_clauses = if complex_clause {
|
||||
Term::var("__other_clauses_delayed".to_string())
|
||||
Term::var("__other_clauses_delayed")
|
||||
} else {
|
||||
term.clone()
|
||||
};
|
||||
|
@ -4692,9 +4692,7 @@ impl<'a> CodeGenerator<'a> {
|
|||
}
|
||||
|
||||
if complex_clause {
|
||||
term = body
|
||||
.lambda("__other_clauses_delayed".to_string())
|
||||
.apply(term.delay());
|
||||
term = body.lambda("__other_clauses_delayed").apply(term.delay());
|
||||
}
|
||||
} else {
|
||||
let condition = if tipo.is_int() {
|
||||
|
@ -4719,12 +4717,9 @@ impl<'a> CodeGenerator<'a> {
|
|||
|
||||
if complex_clause {
|
||||
term = condition
|
||||
.if_else(
|
||||
body.delay(),
|
||||
Term::var("__other_clauses_delayed".to_string()),
|
||||
)
|
||||
.if_else(body.delay(), Term::var("__other_clauses_delayed"))
|
||||
.force()
|
||||
.lambda("__other_clauses_delayed".to_string())
|
||||
.lambda("__other_clauses_delayed")
|
||||
.apply(term.delay());
|
||||
} else {
|
||||
term = condition.delayed_if_else(body, term);
|
||||
|
@ -4754,12 +4749,9 @@ impl<'a> CodeGenerator<'a> {
|
|||
|
||||
if complex_clause {
|
||||
term = Term::var(tail_name)
|
||||
.choose_list(
|
||||
body.delay(),
|
||||
Term::var("__other_clauses_delayed".to_string()),
|
||||
)
|
||||
.choose_list(body.delay(), Term::var("__other_clauses_delayed"))
|
||||
.force()
|
||||
.lambda("__other_clauses_delayed".to_string())
|
||||
.lambda("__other_clauses_delayed")
|
||||
.apply(arg.delay());
|
||||
} else {
|
||||
term = Term::var(tail_name).delayed_choose_list(body, arg);
|
||||
|
@ -4773,9 +4765,7 @@ impl<'a> CodeGenerator<'a> {
|
|||
let mut term = arg_stack.pop().unwrap();
|
||||
let arg = arg_stack.pop().unwrap();
|
||||
|
||||
term = term
|
||||
.lambda("__other_clauses_delayed".to_string())
|
||||
.apply(arg.delay());
|
||||
term = term.lambda("__other_clauses_delayed").apply(arg.delay());
|
||||
|
||||
arg_stack.push(term);
|
||||
}
|
||||
|
@ -4787,7 +4777,7 @@ impl<'a> CodeGenerator<'a> {
|
|||
let then = arg_stack.pop().unwrap();
|
||||
|
||||
if tipo.is_bool() {
|
||||
let mut term = Term::var("__other_clauses_delayed".to_string());
|
||||
let mut term = Term::var("__other_clauses_delayed");
|
||||
if matches!(checker, Term::Constant(boolean) if matches!(boolean.as_ref(), UplcConstant::Bool(true)))
|
||||
{
|
||||
term = Term::var(subject_name).if_else(then.delay(), term).force();
|
||||
|
@ -4817,10 +4807,7 @@ impl<'a> CodeGenerator<'a> {
|
|||
};
|
||||
|
||||
let term = condition
|
||||
.if_else(
|
||||
then.delay(),
|
||||
Term::var("__other_clauses_delayed".to_string()),
|
||||
)
|
||||
.if_else(then.delay(), Term::var("__other_clauses_delayed"))
|
||||
.force();
|
||||
arg_stack.push(term);
|
||||
}
|
||||
|
@ -4847,17 +4834,11 @@ impl<'a> CodeGenerator<'a> {
|
|||
|
||||
if !inverse {
|
||||
term = Term::var(tail_name)
|
||||
.choose_list(
|
||||
term.delay(),
|
||||
Term::var("__other_clauses_delayed".to_string()),
|
||||
)
|
||||
.choose_list(term.delay(), Term::var("__other_clauses_delayed"))
|
||||
.force();
|
||||
} else {
|
||||
term = Term::var(tail_name)
|
||||
.choose_list(
|
||||
Term::var("__other_clauses_delayed".to_string()),
|
||||
term.delay(),
|
||||
)
|
||||
.choose_list(Term::var("__other_clauses_delayed"), term.delay())
|
||||
.force();
|
||||
}
|
||||
|
||||
|
@ -4925,8 +4906,8 @@ impl<'a> CodeGenerator<'a> {
|
|||
self.needs_field_access = true;
|
||||
let constr = arg_stack.pop().unwrap();
|
||||
|
||||
let mut term = Term::var(CONSTR_GET_FIELD.to_string())
|
||||
.apply(Term::var(CONSTR_FIELDS_EXPOSER.to_string()).apply(constr))
|
||||
let mut term = Term::var(CONSTR_GET_FIELD)
|
||||
.apply(Term::var(CONSTR_FIELDS_EXPOSER).apply(constr))
|
||||
.apply(Term::integer(record_index.into()));
|
||||
|
||||
term = convert_data_to_type(term, &tipo);
|
||||
|
@ -4970,7 +4951,7 @@ impl<'a> CodeGenerator<'a> {
|
|||
term
|
||||
};
|
||||
|
||||
term = term.apply(Term::var(CONSTR_FIELDS_EXPOSER.to_string()).apply(value));
|
||||
term = term.apply(Term::var(CONSTR_FIELDS_EXPOSER).apply(value));
|
||||
|
||||
arg_stack.push(term);
|
||||
}
|
||||
|
@ -5032,7 +5013,7 @@ impl<'a> CodeGenerator<'a> {
|
|||
..
|
||||
} => {
|
||||
self.needs_field_access = true;
|
||||
let tail_name_prefix = "__tail_index".to_string();
|
||||
let tail_name_prefix = "__tail_index";
|
||||
|
||||
let record = arg_stack.pop().unwrap();
|
||||
|
||||
|
@ -5108,7 +5089,7 @@ impl<'a> CodeGenerator<'a> {
|
|||
|
||||
term = term
|
||||
.lambda(prev_tail_name)
|
||||
.apply(Term::var(CONSTR_FIELDS_EXPOSER.to_string()).apply(record));
|
||||
.apply(Term::var(CONSTR_FIELDS_EXPOSER).apply(record));
|
||||
|
||||
arg_stack.push(term);
|
||||
}
|
||||
|
@ -5144,7 +5125,7 @@ impl<'a> CodeGenerator<'a> {
|
|||
} else {
|
||||
self.needs_field_access = true;
|
||||
term = convert_data_to_type(
|
||||
Term::var(CONSTR_GET_FIELD.to_string())
|
||||
Term::var(CONSTR_GET_FIELD)
|
||||
.apply(term)
|
||||
.apply(Term::integer(tuple_index.into())),
|
||||
&tipo.get_inner_types()[tuple_index],
|
||||
|
@ -5226,7 +5207,7 @@ impl<'a> CodeGenerator<'a> {
|
|||
let next_clause = arg_stack.pop().unwrap();
|
||||
|
||||
term = term
|
||||
.lambda("__other_clauses_delayed".to_string())
|
||||
.lambda("__other_clauses_delayed")
|
||||
.apply(next_clause.delay());
|
||||
}
|
||||
|
||||
|
|
|
@ -45,3 +45,36 @@ test foo_4() {
|
|||
False
|
||||
}
|
||||
}
|
||||
|
||||
// type Seasons {
|
||||
// Winter
|
||||
// Spring
|
||||
// Summer
|
||||
// Fall
|
||||
// }
|
||||
|
||||
// fn is_cold(season, hour) {
|
||||
// when season is {
|
||||
// Winter | Fall ->
|
||||
// True
|
||||
// _ if hour >= 18 ->
|
||||
// True
|
||||
// _ ->
|
||||
// False
|
||||
// }
|
||||
// }
|
||||
|
||||
// test foo_5() {
|
||||
// !is_cold(Spring, 15) && is_cold(Summer, 22)
|
||||
// }
|
||||
|
||||
fn when_tuple(a: (Int, Int)) -> Int {
|
||||
when a is {
|
||||
(a, _b) ->
|
||||
a
|
||||
}
|
||||
}
|
||||
|
||||
test spend() {
|
||||
when_tuple((4, 1)) == 4
|
||||
}
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
{
|
||||
"preamble": {
|
||||
"title": "aiken-lang/acceptance_test_048",
|
||||
"version": "0.0.0",
|
||||
"plutusVersion": "v2"
|
||||
},
|
||||
"validators": [
|
||||
{
|
||||
"title": "foo.spend",
|
||||
"datum": {
|
||||
"title": "a",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Data"
|
||||
}
|
||||
},
|
||||
"redeemer": {
|
||||
"title": "b",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Data"
|
||||
}
|
||||
},
|
||||
"compiledCode": "586001000032323232323232323222253330063370e6464640046eb4c02c008dd69804800a5ef6c6010104000101010048020526163001001222533300800214984cc014c004c024008ccc00c00cc0280080055cd2b9b5573aaae7955cfaba157441",
|
||||
"hash": "7ecbfc3ae91c4d5ba3799b4d283e385d457c860cd22034d825379ae2"
|
||||
}
|
||||
],
|
||||
"definitions": {
|
||||
"Data": {
|
||||
"title": "Data",
|
||||
"description": "Any Plutus data."
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
fn when_tuple(a: (Int, Int)) -> Int {
|
||||
when a is {
|
||||
(a, b) ->
|
||||
a
|
||||
}
|
||||
}
|
||||
|
||||
validator {
|
||||
fn spend(a: Data, b: Data, c) -> Bool {
|
||||
when_tuple((4, 1)) == 4
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue