feat: expects now print the line of code that failed
This commit is contained in:
parent
412945af3a
commit
355e38d6e2
|
@ -29,7 +29,7 @@ use crate::{
|
||||||
find_and_replace_generics, find_list_clause_or_default_first, get_arg_type_name,
|
find_and_replace_generics, find_list_clause_or_default_first, get_arg_type_name,
|
||||||
get_generic_id_and_type, get_src_code_by_span, get_variant_name, monomorphize,
|
get_generic_id_and_type, get_src_code_by_span, get_variant_name, monomorphize,
|
||||||
pattern_has_conditions, wrap_as_multi_validator, wrap_validator_condition, CodeGenFunction,
|
pattern_has_conditions, wrap_as_multi_validator, wrap_validator_condition, CodeGenFunction,
|
||||||
SpecificClause, CONSTR_INDEX_MISMATCH,
|
SpecificClause,
|
||||||
},
|
},
|
||||||
tipo::{
|
tipo::{
|
||||||
ModuleValueConstructor, PatternConstructor, Type, TypeInfo, ValueConstructor,
|
ModuleValueConstructor, PatternConstructor, Type, TypeInfo, ValueConstructor,
|
||||||
|
@ -44,8 +44,7 @@ use self::{
|
||||||
cast_validator_args, constants_ir, convert_type_to_data, extract_constant,
|
cast_validator_args, constants_ir, convert_type_to_data, extract_constant,
|
||||||
lookup_data_type_by_tipo, modify_cyclic_calls, modify_self_calls, rearrange_list_clauses,
|
lookup_data_type_by_tipo, modify_cyclic_calls, modify_self_calls, rearrange_list_clauses,
|
||||||
AssignmentProperties, ClauseProperties, CodeGenSpecialFuncs, CycleFunctionNames,
|
AssignmentProperties, ClauseProperties, CodeGenSpecialFuncs, CycleFunctionNames,
|
||||||
DataTypeKey, FunctionAccessKey, HoistableFunction, Variant, CONSTR_NOT_EMPTY,
|
DataTypeKey, FunctionAccessKey, HoistableFunction, Variant,
|
||||||
LIST_NOT_EMPTY, TOO_MANY_ITEMS,
|
|
||||||
},
|
},
|
||||||
tree::{AirExpression, AirTree, TreePath},
|
tree::{AirExpression, AirTree, TreePath},
|
||||||
};
|
};
|
||||||
|
@ -133,7 +132,10 @@ impl<'a> CodeGenerator<'a> {
|
||||||
|
|
||||||
air_tree_fun = wrap_validator_condition(air_tree_fun, self.tracing);
|
air_tree_fun = wrap_validator_condition(air_tree_fun, self.tracing);
|
||||||
|
|
||||||
let mut validator_args_tree = self.check_validator_args(&fun.arguments, true, air_tree_fun);
|
let src_code = self.module_src.get(module_name).unwrap().clone();
|
||||||
|
|
||||||
|
let mut validator_args_tree =
|
||||||
|
self.check_validator_args(&fun.arguments, true, air_tree_fun, &src_code);
|
||||||
|
|
||||||
validator_args_tree = AirTree::no_op().hoist_over(validator_args_tree);
|
validator_args_tree = AirTree::no_op().hoist_over(validator_args_tree);
|
||||||
|
|
||||||
|
@ -153,7 +155,7 @@ impl<'a> CodeGenerator<'a> {
|
||||||
air_tree_fun_other = wrap_validator_condition(air_tree_fun_other, self.tracing);
|
air_tree_fun_other = wrap_validator_condition(air_tree_fun_other, self.tracing);
|
||||||
|
|
||||||
let mut validator_args_tree_other =
|
let mut validator_args_tree_other =
|
||||||
self.check_validator_args(&other.arguments, true, air_tree_fun_other);
|
self.check_validator_args(&other.arguments, true, air_tree_fun_other, &src_code);
|
||||||
|
|
||||||
validator_args_tree_other = AirTree::no_op().hoist_over(validator_args_tree_other);
|
validator_args_tree_other = AirTree::no_op().hoist_over(validator_args_tree_other);
|
||||||
|
|
||||||
|
@ -173,8 +175,11 @@ impl<'a> CodeGenerator<'a> {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Special Case with multi_validators
|
// Special Case with multi_validators
|
||||||
self.special_functions.use_function(CONSTR_FIELDS_EXPOSER);
|
self.special_functions
|
||||||
self.special_functions.use_function(CONSTR_INDEX_EXPOSER);
|
.use_function_uplc(CONSTR_FIELDS_EXPOSER.to_string());
|
||||||
|
|
||||||
|
self.special_functions
|
||||||
|
.use_function_uplc(CONSTR_INDEX_EXPOSER.to_string());
|
||||||
|
|
||||||
term = wrap_as_multi_validator(spend, mint, self.tracing, spend_name, mint_name);
|
term = wrap_as_multi_validator(spend, mint, self.tracing, spend_name, mint_name);
|
||||||
}
|
}
|
||||||
|
@ -463,6 +468,22 @@ impl<'a> CodeGenerator<'a> {
|
||||||
|
|
||||||
let air_value = self.build(value, module_name);
|
let air_value = self.build(value, module_name);
|
||||||
|
|
||||||
|
let msg = get_src_code_by_span(module_name, location, &self.module_src);
|
||||||
|
|
||||||
|
let msg_func_name = msg.split_whitespace().join("");
|
||||||
|
|
||||||
|
self.special_functions.insert_new_function(
|
||||||
|
msg_func_name.clone(),
|
||||||
|
Term::string(msg),
|
||||||
|
string(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let msg_func = if self.tracing {
|
||||||
|
self.special_functions.use_function_tree(msg_func_name)
|
||||||
|
} else {
|
||||||
|
AirTree::no_op()
|
||||||
|
};
|
||||||
|
|
||||||
self.assignment(
|
self.assignment(
|
||||||
pattern,
|
pattern,
|
||||||
air_value,
|
air_value,
|
||||||
|
@ -472,8 +493,7 @@ impl<'a> CodeGenerator<'a> {
|
||||||
kind: *kind,
|
kind: *kind,
|
||||||
remove_unused: kind.is_let(),
|
remove_unused: kind.is_let(),
|
||||||
full_check: !tipo.is_data() && value.tipo().is_data() && kind.is_expect(),
|
full_check: !tipo.is_data() && value.tipo().is_data() && kind.is_expect(),
|
||||||
location: *location,
|
msg_func,
|
||||||
module_name: module_name.clone(),
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -514,8 +534,7 @@ impl<'a> CodeGenerator<'a> {
|
||||||
kind: AssignmentKind::Let,
|
kind: AssignmentKind::Let,
|
||||||
remove_unused: false,
|
remove_unused: false,
|
||||||
full_check: false,
|
full_check: false,
|
||||||
location: Span::empty(),
|
msg_func: AirTree::no_op(),
|
||||||
module_name: module_name.clone(),
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -621,10 +640,8 @@ impl<'a> CodeGenerator<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let list_of_fields = AirTree::call(
|
let list_of_fields = AirTree::call(
|
||||||
AirTree::local_var(
|
self.special_functions
|
||||||
self.special_functions.use_function(CONSTR_FIELDS_EXPOSER),
|
.use_function_tree(CONSTR_FIELDS_EXPOSER.to_string()),
|
||||||
void(),
|
|
||||||
),
|
|
||||||
list(data()),
|
list(data()),
|
||||||
vec![self.build(record, module_name)],
|
vec![self.build(record, module_name)],
|
||||||
);
|
);
|
||||||
|
@ -828,10 +845,8 @@ impl<'a> CodeGenerator<'a> {
|
||||||
AirTree::local_var(name, int()),
|
AirTree::local_var(name, int()),
|
||||||
int(),
|
int(),
|
||||||
);
|
);
|
||||||
let msg =
|
|
||||||
get_src_code_by_span(&props.module_name, &props.location, &self.module_src);
|
|
||||||
|
|
||||||
AirTree::assert_bool(true, assignment.hoist_over(expect), msg)
|
AirTree::assert_bool(true, assignment.hoist_over(expect), props.msg_func.clone())
|
||||||
}
|
}
|
||||||
Pattern::Var { name, .. } => {
|
Pattern::Var { name, .. } => {
|
||||||
if props.full_check {
|
if props.full_check {
|
||||||
|
@ -851,6 +866,7 @@ impl<'a> CodeGenerator<'a> {
|
||||||
val,
|
val,
|
||||||
&mut index_map,
|
&mut index_map,
|
||||||
pattern.location(),
|
pattern.location(),
|
||||||
|
props.msg_func,
|
||||||
);
|
);
|
||||||
|
|
||||||
let assign_expect = AirTree::let_assignment("_", expect);
|
let assign_expect = AirTree::let_assignment("_", expect);
|
||||||
|
@ -890,6 +906,7 @@ impl<'a> CodeGenerator<'a> {
|
||||||
val,
|
val,
|
||||||
&mut index_map,
|
&mut index_map,
|
||||||
pattern.location(),
|
pattern.location(),
|
||||||
|
props.msg_func,
|
||||||
);
|
);
|
||||||
|
|
||||||
let assign_expect = AirTree::let_assignment("_", expect);
|
let assign_expect = AirTree::let_assignment("_", expect);
|
||||||
|
@ -948,8 +965,7 @@ impl<'a> CodeGenerator<'a> {
|
||||||
kind: props.kind,
|
kind: props.kind,
|
||||||
remove_unused: true,
|
remove_unused: true,
|
||||||
full_check: props.full_check,
|
full_check: props.full_check,
|
||||||
location: props.location,
|
msg_func: props.msg_func.clone(),
|
||||||
module_name: props.module_name.clone(),
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
@ -991,8 +1007,7 @@ impl<'a> CodeGenerator<'a> {
|
||||||
kind: props.kind,
|
kind: props.kind,
|
||||||
remove_unused: true,
|
remove_unused: true,
|
||||||
full_check: props.full_check,
|
full_check: props.full_check,
|
||||||
location: props.location,
|
msg_func: props.msg_func.clone(),
|
||||||
module_name: props.module_name.clone(),
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
@ -1005,9 +1020,15 @@ impl<'a> CodeGenerator<'a> {
|
||||||
let names = elems.iter().map(|(name, _)| name.to_string()).collect_vec();
|
let names = elems.iter().map(|(name, _)| name.to_string()).collect_vec();
|
||||||
|
|
||||||
let list_access = if elements.is_empty() {
|
let list_access = if elements.is_empty() {
|
||||||
AirTree::list_empty(value)
|
AirTree::list_empty(value, props.msg_func)
|
||||||
} else {
|
} else {
|
||||||
AirTree::list_access(names, tipo.clone(), tail.is_some(), tail.is_none(), value)
|
AirTree::list_access(
|
||||||
|
names,
|
||||||
|
tipo.clone(),
|
||||||
|
tail.is_some(),
|
||||||
|
value,
|
||||||
|
Some(props.msg_func),
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut sequence = vec![list_access];
|
let mut sequence = vec![list_access];
|
||||||
|
@ -1027,10 +1048,7 @@ impl<'a> CodeGenerator<'a> {
|
||||||
if tipo.is_bool() {
|
if tipo.is_bool() {
|
||||||
assert!(props.kind.is_expect());
|
assert!(props.kind.is_expect());
|
||||||
|
|
||||||
let msg =
|
AirTree::assert_bool(name == "True", value, props.msg_func)
|
||||||
get_src_code_by_span(&props.module_name, &props.location, &self.module_src);
|
|
||||||
|
|
||||||
AirTree::assert_bool(name == "True", value, msg)
|
|
||||||
} else if tipo.is_void() {
|
} else if tipo.is_void() {
|
||||||
AirTree::let_assignment("_", value)
|
AirTree::let_assignment("_", value)
|
||||||
} else {
|
} else {
|
||||||
|
@ -1062,16 +1080,10 @@ impl<'a> CodeGenerator<'a> {
|
||||||
|
|
||||||
sequence.push(constructor_val);
|
sequence.push(constructor_val);
|
||||||
|
|
||||||
let msg = get_src_code_by_span(
|
|
||||||
&props.module_name,
|
|
||||||
&props.location,
|
|
||||||
&self.module_src,
|
|
||||||
);
|
|
||||||
|
|
||||||
let assert_constr = AirTree::assert_constr_index(
|
let assert_constr = AirTree::assert_constr_index(
|
||||||
index,
|
index,
|
||||||
AirTree::local_var(&constructor_name, tipo.clone()),
|
AirTree::local_var(&constructor_name, tipo.clone()),
|
||||||
msg,
|
props.msg_func.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
sequence.push(assert_constr);
|
sequence.push(assert_constr);
|
||||||
|
@ -1147,8 +1159,7 @@ impl<'a> CodeGenerator<'a> {
|
||||||
kind: props.kind,
|
kind: props.kind,
|
||||||
remove_unused: true,
|
remove_unused: true,
|
||||||
full_check: props.full_check,
|
full_check: props.full_check,
|
||||||
location: props.location,
|
msg_func: props.msg_func.clone(),
|
||||||
module_name: props.module_name.clone(),
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
@ -1169,7 +1180,12 @@ impl<'a> CodeGenerator<'a> {
|
||||||
if check_replaceable_opaque_type(tipo, &self.data_types) {
|
if check_replaceable_opaque_type(tipo, &self.data_types) {
|
||||||
sequence.push(AirTree::let_assignment(&indices[0].1, value));
|
sequence.push(AirTree::let_assignment(&indices[0].1, value));
|
||||||
} else {
|
} else {
|
||||||
sequence.push(AirTree::fields_expose(indices, props.full_check, value));
|
let msg = if props.full_check {
|
||||||
|
Some(props.msg_func)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
sequence.push(AirTree::fields_expose(indices, value, msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
sequence.append(
|
sequence.append(
|
||||||
|
@ -1237,8 +1253,7 @@ impl<'a> CodeGenerator<'a> {
|
||||||
kind: props.kind,
|
kind: props.kind,
|
||||||
remove_unused: true,
|
remove_unused: true,
|
||||||
full_check: props.full_check,
|
full_check: props.full_check,
|
||||||
location: props.location,
|
msg_func: props.msg_func.clone(),
|
||||||
module_name: props.module_name.clone(),
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
@ -1251,14 +1266,15 @@ impl<'a> CodeGenerator<'a> {
|
||||||
|
|
||||||
let indices = elems.iter().map(|(name, _)| name.to_string()).collect_vec();
|
let indices = elems.iter().map(|(name, _)| name.to_string()).collect_vec();
|
||||||
|
|
||||||
|
let msg = if props.full_check {
|
||||||
|
Some(props.msg_func)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
// This `value` is either value param that was passed in or
|
// This `value` is either value param that was passed in or
|
||||||
// local var
|
// local var
|
||||||
sequence.push(AirTree::tuple_access(
|
sequence.push(AirTree::tuple_access(indices, tipo.clone(), value, msg));
|
||||||
indices,
|
|
||||||
tipo.clone(),
|
|
||||||
props.full_check,
|
|
||||||
value,
|
|
||||||
));
|
|
||||||
|
|
||||||
sequence.append(&mut elems.into_iter().map(|(_, field)| field).collect_vec());
|
sequence.append(&mut elems.into_iter().map(|(_, field)| field).collect_vec());
|
||||||
|
|
||||||
|
@ -1273,6 +1289,7 @@ impl<'a> CodeGenerator<'a> {
|
||||||
value: AirTree,
|
value: AirTree,
|
||||||
defined_data_types: &mut IndexMap<String, u64>,
|
defined_data_types: &mut IndexMap<String, u64>,
|
||||||
location: Span,
|
location: Span,
|
||||||
|
msg_func: AirTree,
|
||||||
) -> AirTree {
|
) -> AirTree {
|
||||||
assert!(tipo.get_generic().is_none());
|
assert!(tipo.get_generic().is_none());
|
||||||
let tipo = &convert_opaque_type(tipo, &self.data_types);
|
let tipo = &convert_opaque_type(tipo, &self.data_types);
|
||||||
|
@ -1298,8 +1315,8 @@ impl<'a> CodeGenerator<'a> {
|
||||||
let tuple_access = AirTree::tuple_access(
|
let tuple_access = AirTree::tuple_access(
|
||||||
vec![fst_name.clone(), snd_name.clone()],
|
vec![fst_name.clone(), snd_name.clone()],
|
||||||
inner_list_type.clone(),
|
inner_list_type.clone(),
|
||||||
false,
|
|
||||||
AirTree::local_var(&pair_name, inner_list_type.clone()),
|
AirTree::local_var(&pair_name, inner_list_type.clone()),
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
let expect_fst = self.expect_type_assign(
|
let expect_fst = self.expect_type_assign(
|
||||||
|
@ -1307,6 +1324,7 @@ impl<'a> CodeGenerator<'a> {
|
||||||
AirTree::local_var(fst_name, inner_pair_types[0].clone()),
|
AirTree::local_var(fst_name, inner_pair_types[0].clone()),
|
||||||
defined_data_types,
|
defined_data_types,
|
||||||
location,
|
location,
|
||||||
|
msg_func.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let expect_snd = self.expect_type_assign(
|
let expect_snd = self.expect_type_assign(
|
||||||
|
@ -1314,6 +1332,7 @@ impl<'a> CodeGenerator<'a> {
|
||||||
AirTree::local_var(snd_name, inner_pair_types[1].clone()),
|
AirTree::local_var(snd_name, inner_pair_types[1].clone()),
|
||||||
defined_data_types,
|
defined_data_types,
|
||||||
location,
|
location,
|
||||||
|
msg_func.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let anon_func_body = AirTree::UnhoistedSequence(vec![
|
let anon_func_body = AirTree::UnhoistedSequence(vec![
|
||||||
|
@ -1385,6 +1404,7 @@ impl<'a> CodeGenerator<'a> {
|
||||||
),
|
),
|
||||||
defined_data_types,
|
defined_data_types,
|
||||||
location,
|
location,
|
||||||
|
msg_func,
|
||||||
);
|
);
|
||||||
|
|
||||||
let anon_func_body = expect_item;
|
let anon_func_body = expect_item;
|
||||||
|
@ -1447,8 +1467,8 @@ impl<'a> CodeGenerator<'a> {
|
||||||
let tuple_access = AirTree::tuple_access(
|
let tuple_access = AirTree::tuple_access(
|
||||||
vec![fst_name.clone(), snd_name.clone()],
|
vec![fst_name.clone(), snd_name.clone()],
|
||||||
tipo.clone(),
|
tipo.clone(),
|
||||||
false,
|
|
||||||
AirTree::local_var(pair_name, tipo.clone()),
|
AirTree::local_var(pair_name, tipo.clone()),
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
let expect_fst = self.expect_type_assign(
|
let expect_fst = self.expect_type_assign(
|
||||||
|
@ -1456,6 +1476,7 @@ impl<'a> CodeGenerator<'a> {
|
||||||
AirTree::local_var(fst_name, tuple_inner_types[0].clone()),
|
AirTree::local_var(fst_name, tuple_inner_types[0].clone()),
|
||||||
defined_data_types,
|
defined_data_types,
|
||||||
location,
|
location,
|
||||||
|
msg_func.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let expect_snd = self.expect_type_assign(
|
let expect_snd = self.expect_type_assign(
|
||||||
|
@ -1463,6 +1484,7 @@ impl<'a> CodeGenerator<'a> {
|
||||||
AirTree::local_var(snd_name, tuple_inner_types[1].clone()),
|
AirTree::local_var(snd_name, tuple_inner_types[1].clone()),
|
||||||
defined_data_types,
|
defined_data_types,
|
||||||
location,
|
location,
|
||||||
|
msg_func,
|
||||||
);
|
);
|
||||||
|
|
||||||
AirTree::UnhoistedSequence(vec![
|
AirTree::UnhoistedSequence(vec![
|
||||||
|
@ -1493,6 +1515,7 @@ impl<'a> CodeGenerator<'a> {
|
||||||
AirTree::local_var(&tuple_index_name, arg.clone()),
|
AirTree::local_var(&tuple_index_name, arg.clone()),
|
||||||
defined_data_types,
|
defined_data_types,
|
||||||
location,
|
location,
|
||||||
|
msg_func.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
(
|
(
|
||||||
|
@ -1510,8 +1533,8 @@ impl<'a> CodeGenerator<'a> {
|
||||||
let tuple_access = AirTree::tuple_access(
|
let tuple_access = AirTree::tuple_access(
|
||||||
tuple_index_names,
|
tuple_index_names,
|
||||||
tipo.clone(),
|
tipo.clone(),
|
||||||
true,
|
|
||||||
AirTree::local_var(tuple_name, tipo.clone()),
|
AirTree::local_var(tuple_name, tipo.clone()),
|
||||||
|
Some(msg_func),
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut tuple_expects = tuple_expect_items
|
let mut tuple_expects = tuple_expect_items
|
||||||
|
@ -1553,10 +1576,7 @@ impl<'a> CodeGenerator<'a> {
|
||||||
|
|
||||||
let error_term = if self.tracing {
|
let error_term = if self.tracing {
|
||||||
AirTree::trace(
|
AirTree::trace(
|
||||||
AirTree::local_var(
|
msg_func.clone(),
|
||||||
self.special_functions.use_function(CONSTR_INDEX_MISMATCH),
|
|
||||||
string(),
|
|
||||||
),
|
|
||||||
tipo.clone(),
|
tipo.clone(),
|
||||||
AirTree::error(tipo.clone(), false),
|
AirTree::error(tipo.clone(), false),
|
||||||
)
|
)
|
||||||
|
@ -1590,6 +1610,7 @@ impl<'a> CodeGenerator<'a> {
|
||||||
AirTree::local_var(&arg_name, arg_tipo.clone()),
|
AirTree::local_var(&arg_name, arg_tipo.clone()),
|
||||||
defined_data_types,
|
defined_data_types,
|
||||||
location,
|
location,
|
||||||
|
msg_func.clone(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1610,16 +1631,7 @@ impl<'a> CodeGenerator<'a> {
|
||||||
.collect_vec();
|
.collect_vec();
|
||||||
|
|
||||||
if assigns.is_empty() {
|
if assigns.is_empty() {
|
||||||
let empty = AirTree::fields_empty(AirTree::local_var(
|
let empty = AirTree::fields_empty(
|
||||||
format!("__constr_var_span_{}_{}", location.start, location.end),
|
|
||||||
tipo.clone(),
|
|
||||||
));
|
|
||||||
|
|
||||||
assigns.insert(0, empty);
|
|
||||||
} else {
|
|
||||||
let expose = AirTree::fields_expose(
|
|
||||||
indices,
|
|
||||||
true,
|
|
||||||
AirTree::local_var(
|
AirTree::local_var(
|
||||||
format!(
|
format!(
|
||||||
"__constr_var_span_{}_{}",
|
"__constr_var_span_{}_{}",
|
||||||
|
@ -1627,6 +1639,21 @@ impl<'a> CodeGenerator<'a> {
|
||||||
),
|
),
|
||||||
tipo.clone(),
|
tipo.clone(),
|
||||||
),
|
),
|
||||||
|
msg_func.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
|
assigns.insert(0, empty);
|
||||||
|
} else {
|
||||||
|
let expose = AirTree::fields_expose(
|
||||||
|
indices,
|
||||||
|
AirTree::local_var(
|
||||||
|
format!(
|
||||||
|
"__constr_var_span_{}_{}",
|
||||||
|
location.start, location.end
|
||||||
|
),
|
||||||
|
tipo.clone(),
|
||||||
|
),
|
||||||
|
Some(msg_func.clone()),
|
||||||
);
|
);
|
||||||
|
|
||||||
assigns.insert(0, expose);
|
assigns.insert(0, expose);
|
||||||
|
@ -2205,8 +2232,11 @@ impl<'a> CodeGenerator<'a> {
|
||||||
defined_heads,
|
defined_heads,
|
||||||
subject_tipo.clone(),
|
subject_tipo.clone(),
|
||||||
tail.is_some(),
|
tail.is_some(),
|
||||||
false,
|
|
||||||
AirTree::local_var(&props.original_subject_name, subject_tipo.clone()),
|
AirTree::local_var(&props.original_subject_name, subject_tipo.clone()),
|
||||||
|
// One special usage of list access here
|
||||||
|
// So for the msg we pass in empty string if tracing is on
|
||||||
|
// Since check_last_item is false this will never get added to the final uplc anyway
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
assert!(defined_tails.len() >= defined_heads.len());
|
assert!(defined_tails.len() >= defined_heads.len());
|
||||||
|
@ -2345,11 +2375,11 @@ impl<'a> CodeGenerator<'a> {
|
||||||
} else {
|
} else {
|
||||||
AirTree::fields_expose(
|
AirTree::fields_expose(
|
||||||
indices,
|
indices,
|
||||||
false,
|
|
||||||
AirTree::local_var(
|
AirTree::local_var(
|
||||||
props.clause_var_name.clone(),
|
props.clause_var_name.clone(),
|
||||||
subject_tipo.clone(),
|
subject_tipo.clone(),
|
||||||
),
|
),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2482,8 +2512,8 @@ impl<'a> CodeGenerator<'a> {
|
||||||
AirTree::tuple_access(
|
AirTree::tuple_access(
|
||||||
names,
|
names,
|
||||||
subject_tipo.clone(),
|
subject_tipo.clone(),
|
||||||
false,
|
|
||||||
AirTree::local_var(&props.original_subject_name, subject_tipo.clone()),
|
AirTree::local_var(&props.original_subject_name, subject_tipo.clone()),
|
||||||
|
None,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2668,17 +2698,39 @@ impl<'a> CodeGenerator<'a> {
|
||||||
arguments: &[TypedArg],
|
arguments: &[TypedArg],
|
||||||
has_context: bool,
|
has_context: bool,
|
||||||
body: AirTree,
|
body: AirTree,
|
||||||
|
src_code: &str,
|
||||||
) -> AirTree {
|
) -> AirTree {
|
||||||
let checked_args = arguments
|
let checked_args = arguments
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(index, arg)| {
|
.map(|(index, arg)| {
|
||||||
let arg_name = arg.arg_name.get_variable_name().unwrap_or("_").to_string();
|
let arg_name = arg.arg_name.get_variable_name().unwrap_or("_").to_string();
|
||||||
|
let arg_span = arg.location;
|
||||||
|
|
||||||
if !(has_context && index == arguments.len() - 1) && &arg_name != "_" {
|
if !(has_context && index == arguments.len() - 1) && &arg_name != "_" {
|
||||||
let param = AirTree::local_var(&arg_name, data());
|
let param = AirTree::local_var(&arg_name, data());
|
||||||
|
|
||||||
let actual_type = convert_opaque_type(&arg.tipo, &self.data_types);
|
let actual_type = convert_opaque_type(&arg.tipo, &self.data_types);
|
||||||
|
|
||||||
|
let msg = src_code
|
||||||
|
.get(arg_span.start..arg_span.end)
|
||||||
|
.expect("Out of bounds span")
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let msg_func_name = msg.split_whitespace().join("");
|
||||||
|
|
||||||
|
self.special_functions.insert_new_function(
|
||||||
|
msg_func_name.to_string(),
|
||||||
|
Term::string(msg),
|
||||||
|
string(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let msg_func = if self.tracing {
|
||||||
|
self.special_functions.use_function_tree(msg_func_name)
|
||||||
|
} else {
|
||||||
|
AirTree::no_op()
|
||||||
|
};
|
||||||
|
|
||||||
let assign = self.assignment(
|
let assign = self.assignment(
|
||||||
&Pattern::Var {
|
&Pattern::Var {
|
||||||
location: Span::empty(),
|
location: Span::empty(),
|
||||||
|
@ -2691,8 +2743,7 @@ impl<'a> CodeGenerator<'a> {
|
||||||
kind: AssignmentKind::Expect,
|
kind: AssignmentKind::Expect,
|
||||||
remove_unused: false,
|
remove_unused: false,
|
||||||
full_check: true,
|
full_check: true,
|
||||||
location: Span::empty(),
|
msg_func,
|
||||||
module_name: String::new(),
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -3911,9 +3962,17 @@ impl<'a> CodeGenerator<'a> {
|
||||||
// tipo here refers to the list type while the actual return
|
// tipo here refers to the list type while the actual return
|
||||||
// type is nothing since this is an assignment over some expression
|
// type is nothing since this is an assignment over some expression
|
||||||
tipo,
|
tipo,
|
||||||
check_last_item,
|
is_expect,
|
||||||
} => {
|
} => {
|
||||||
let value = arg_stack.pop().unwrap();
|
let value = arg_stack.pop().unwrap();
|
||||||
|
|
||||||
|
let error_term = if self.tracing && is_expect {
|
||||||
|
let msg = arg_stack.pop().unwrap();
|
||||||
|
Term::Error.delayed_trace(msg)
|
||||||
|
} else {
|
||||||
|
Term::Error
|
||||||
|
};
|
||||||
|
|
||||||
let mut term = arg_stack.pop().unwrap();
|
let mut term = arg_stack.pop().unwrap();
|
||||||
|
|
||||||
let list_id = self.id_gen.next();
|
let list_id = self.id_gen.next();
|
||||||
|
@ -3937,37 +3996,21 @@ impl<'a> CodeGenerator<'a> {
|
||||||
.collect_vec();
|
.collect_vec();
|
||||||
|
|
||||||
if !names_empty {
|
if !names_empty {
|
||||||
let error_term = if self.tracing {
|
|
||||||
Term::Error.delayed_trace(Term::var(
|
|
||||||
self.special_functions.use_function(TOO_MANY_ITEMS),
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
Term::Error
|
|
||||||
};
|
|
||||||
|
|
||||||
term = builder::list_access_to_uplc(
|
term = builder::list_access_to_uplc(
|
||||||
&names_types,
|
&names_types,
|
||||||
&id_list,
|
&id_list,
|
||||||
tail,
|
tail,
|
||||||
0,
|
0,
|
||||||
term,
|
term,
|
||||||
check_last_item,
|
is_expect && !tail,
|
||||||
true,
|
true,
|
||||||
error_term,
|
error_term,
|
||||||
)
|
)
|
||||||
.apply(value);
|
.apply(value);
|
||||||
|
|
||||||
arg_stack.push(term);
|
arg_stack.push(term);
|
||||||
} else if check_last_item {
|
} else if is_expect {
|
||||||
let trace_term = if self.tracing {
|
term = value.delayed_choose_list(term, error_term);
|
||||||
Term::Error.delayed_trace(Term::var(
|
|
||||||
self.special_functions.use_function(LIST_NOT_EMPTY),
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
Term::Error
|
|
||||||
};
|
|
||||||
|
|
||||||
term = value.delayed_choose_list(term, trace_term);
|
|
||||||
|
|
||||||
arg_stack.push(term);
|
arg_stack.push(term);
|
||||||
} else {
|
} else {
|
||||||
|
@ -4449,37 +4492,44 @@ impl<'a> CodeGenerator<'a> {
|
||||||
|
|
||||||
arg_stack.push(term);
|
arg_stack.push(term);
|
||||||
}
|
}
|
||||||
Air::AssertConstr { constr_index, msg } => {
|
Air::AssertConstr { constr_index } => {
|
||||||
let constr = arg_stack.pop().unwrap();
|
let constr = arg_stack.pop().unwrap();
|
||||||
|
|
||||||
let mut term = arg_stack.pop().unwrap();
|
|
||||||
|
|
||||||
let trace_term = if self.tracing {
|
let trace_term = if self.tracing {
|
||||||
Term::Error.delayed_trace(Term::string(msg))
|
let msg = arg_stack.pop().unwrap();
|
||||||
|
Term::Error.delayed_trace(msg)
|
||||||
} else {
|
} else {
|
||||||
Term::Error
|
Term::Error
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut term = arg_stack.pop().unwrap();
|
||||||
|
|
||||||
term = Term::equals_integer()
|
term = Term::equals_integer()
|
||||||
.apply(Term::integer(constr_index.into()))
|
.apply(Term::integer(constr_index.into()))
|
||||||
.apply(
|
.apply(
|
||||||
Term::var(self.special_functions.use_function(CONSTR_INDEX_EXPOSER))
|
Term::var(
|
||||||
.apply(constr),
|
self.special_functions
|
||||||
|
.use_function_uplc(CONSTR_INDEX_EXPOSER.to_string()),
|
||||||
|
)
|
||||||
|
.apply(constr),
|
||||||
)
|
)
|
||||||
.delayed_if_then_else(term, trace_term);
|
.delayed_if_then_else(term, trace_term);
|
||||||
|
|
||||||
arg_stack.push(term);
|
arg_stack.push(term);
|
||||||
}
|
}
|
||||||
Air::AssertBool { is_true, msg } => {
|
Air::AssertBool { is_true } => {
|
||||||
let value = arg_stack.pop().unwrap();
|
let value = arg_stack.pop().unwrap();
|
||||||
let mut term = arg_stack.pop().unwrap();
|
|
||||||
|
|
||||||
let trace_term = if self.tracing {
|
let trace_term = if self.tracing {
|
||||||
Term::Error.delayed_trace(Term::string(msg))
|
let msg = arg_stack.pop().unwrap();
|
||||||
|
|
||||||
|
Term::Error.delayed_trace(msg)
|
||||||
} else {
|
} else {
|
||||||
Term::Error
|
Term::Error
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut term = arg_stack.pop().unwrap();
|
||||||
|
|
||||||
if is_true {
|
if is_true {
|
||||||
term = value.delayed_if_then_else(term, trace_term)
|
term = value.delayed_if_then_else(term, trace_term)
|
||||||
} else {
|
} else {
|
||||||
|
@ -4504,8 +4554,11 @@ impl<'a> CodeGenerator<'a> {
|
||||||
{
|
{
|
||||||
subject
|
subject
|
||||||
} else {
|
} else {
|
||||||
Term::var(self.special_functions.use_function(CONSTR_INDEX_EXPOSER))
|
Term::var(
|
||||||
.apply(subject)
|
self.special_functions
|
||||||
|
.use_function_uplc(CONSTR_INDEX_EXPOSER.to_string()),
|
||||||
|
)
|
||||||
|
.apply(subject)
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut term = arg_stack.pop().unwrap();
|
let mut term = arg_stack.pop().unwrap();
|
||||||
|
@ -4710,8 +4763,11 @@ impl<'a> CodeGenerator<'a> {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
} else {
|
} else {
|
||||||
Term::equals_integer().apply(checker).apply(
|
Term::equals_integer().apply(checker).apply(
|
||||||
Term::var(self.special_functions.use_function(CONSTR_INDEX_EXPOSER))
|
Term::var(
|
||||||
.apply(Term::var(subject_name)),
|
self.special_functions
|
||||||
|
.use_function_uplc(CONSTR_INDEX_EXPOSER.to_string()),
|
||||||
|
)
|
||||||
|
.apply(Term::var(subject_name)),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4850,13 +4906,18 @@ impl<'a> CodeGenerator<'a> {
|
||||||
|
|
||||||
arg_stack.push(term);
|
arg_stack.push(term);
|
||||||
}
|
}
|
||||||
Air::FieldsExpose {
|
Air::FieldsExpose { indices, is_expect } => {
|
||||||
indices,
|
|
||||||
check_last_item,
|
|
||||||
} => {
|
|
||||||
let mut id_list = vec![];
|
let mut id_list = vec![];
|
||||||
|
|
||||||
let value = arg_stack.pop().unwrap();
|
let value = arg_stack.pop().unwrap();
|
||||||
|
|
||||||
|
let error_term = if self.tracing && is_expect {
|
||||||
|
let msg = arg_stack.pop().unwrap();
|
||||||
|
Term::Error.delayed_trace(msg)
|
||||||
|
} else {
|
||||||
|
Term::Error
|
||||||
|
};
|
||||||
|
|
||||||
let mut term = arg_stack.pop().unwrap();
|
let mut term = arg_stack.pop().unwrap();
|
||||||
let list_id = self.id_gen.next();
|
let list_id = self.id_gen.next();
|
||||||
|
|
||||||
|
@ -4875,43 +4936,33 @@ impl<'a> CodeGenerator<'a> {
|
||||||
.collect_vec();
|
.collect_vec();
|
||||||
|
|
||||||
if !indices.is_empty() {
|
if !indices.is_empty() {
|
||||||
let error_term = if self.tracing {
|
|
||||||
Term::Error.delayed_trace(Term::var(
|
|
||||||
self.special_functions.use_function(TOO_MANY_ITEMS),
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
Term::Error
|
|
||||||
};
|
|
||||||
|
|
||||||
term = builder::list_access_to_uplc(
|
term = builder::list_access_to_uplc(
|
||||||
&names_types,
|
&names_types,
|
||||||
&id_list,
|
&id_list,
|
||||||
false,
|
false,
|
||||||
current_index,
|
current_index,
|
||||||
term,
|
term,
|
||||||
check_last_item,
|
is_expect,
|
||||||
false,
|
false,
|
||||||
error_term,
|
error_term,
|
||||||
);
|
);
|
||||||
|
|
||||||
term = term.apply(
|
term = term.apply(
|
||||||
Term::var(self.special_functions.use_function(CONSTR_FIELDS_EXPOSER))
|
Term::var(
|
||||||
.apply(value),
|
self.special_functions
|
||||||
|
.use_function_uplc(CONSTR_FIELDS_EXPOSER.to_string()),
|
||||||
|
)
|
||||||
|
.apply(value),
|
||||||
);
|
);
|
||||||
|
|
||||||
arg_stack.push(term);
|
arg_stack.push(term);
|
||||||
} else if check_last_item {
|
} else if is_expect {
|
||||||
let trace_term = if self.tracing {
|
term = Term::var(
|
||||||
Term::Error.delayed_trace(Term::var(
|
self.special_functions
|
||||||
self.special_functions.use_function(CONSTR_NOT_EMPTY),
|
.use_function_uplc(CONSTR_FIELDS_EXPOSER.to_string()),
|
||||||
))
|
)
|
||||||
} else {
|
.apply(value)
|
||||||
Term::Error
|
.delayed_choose_list(term, error_term);
|
||||||
};
|
|
||||||
|
|
||||||
term = Term::var(self.special_functions.use_function(CONSTR_FIELDS_EXPOSER))
|
|
||||||
.apply(value)
|
|
||||||
.delayed_choose_list(term, trace_term);
|
|
||||||
|
|
||||||
arg_stack.push(term);
|
arg_stack.push(term);
|
||||||
} else {
|
} else {
|
||||||
|
@ -4920,35 +4971,38 @@ impl<'a> CodeGenerator<'a> {
|
||||||
}
|
}
|
||||||
Air::FieldsEmpty => {
|
Air::FieldsEmpty => {
|
||||||
let value = arg_stack.pop().unwrap();
|
let value = arg_stack.pop().unwrap();
|
||||||
let mut term = arg_stack.pop().unwrap();
|
|
||||||
|
|
||||||
let trace_term = if self.tracing {
|
let error_term = if self.tracing {
|
||||||
Term::Error.delayed_trace(Term::var(
|
let msg = arg_stack.pop().unwrap();
|
||||||
self.special_functions.use_function(CONSTR_NOT_EMPTY),
|
Term::Error.delayed_trace(msg)
|
||||||
))
|
|
||||||
} else {
|
} else {
|
||||||
Term::Error
|
Term::Error
|
||||||
};
|
};
|
||||||
|
|
||||||
term = Term::var(self.special_functions.use_function(CONSTR_FIELDS_EXPOSER))
|
let mut term = arg_stack.pop().unwrap();
|
||||||
.apply(value)
|
|
||||||
.delayed_choose_list(term, trace_term);
|
term = Term::var(
|
||||||
|
self.special_functions
|
||||||
|
.use_function_uplc(CONSTR_FIELDS_EXPOSER.to_string()),
|
||||||
|
)
|
||||||
|
.apply(value)
|
||||||
|
.delayed_choose_list(term, error_term);
|
||||||
|
|
||||||
arg_stack.push(term);
|
arg_stack.push(term);
|
||||||
}
|
}
|
||||||
Air::ListEmpty => {
|
Air::ListEmpty => {
|
||||||
let value = arg_stack.pop().unwrap();
|
let value = arg_stack.pop().unwrap();
|
||||||
let mut term = arg_stack.pop().unwrap();
|
|
||||||
|
|
||||||
let trace_term = if self.tracing {
|
let error_term = if self.tracing {
|
||||||
Term::Error.delayed_trace(Term::var(
|
let msg = arg_stack.pop().unwrap();
|
||||||
self.special_functions.use_function(LIST_NOT_EMPTY),
|
Term::Error.delayed_trace(msg)
|
||||||
))
|
|
||||||
} else {
|
} else {
|
||||||
Term::Error
|
Term::Error
|
||||||
};
|
};
|
||||||
|
|
||||||
term = value.delayed_choose_list(term, trace_term);
|
let mut term = arg_stack.pop().unwrap();
|
||||||
|
|
||||||
|
term = value.delayed_choose_list(term, error_term);
|
||||||
|
|
||||||
arg_stack.push(term);
|
arg_stack.push(term);
|
||||||
}
|
}
|
||||||
|
@ -5095,8 +5149,11 @@ impl<'a> CodeGenerator<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
term = term.lambda(format!("{tail_name_prefix}_0")).apply(
|
term = term.lambda(format!("{tail_name_prefix}_0")).apply(
|
||||||
Term::var(self.special_functions.use_function(CONSTR_FIELDS_EXPOSER))
|
Term::var(
|
||||||
.apply(record),
|
self.special_functions
|
||||||
|
.use_function_uplc(CONSTR_FIELDS_EXPOSER.to_string()),
|
||||||
|
)
|
||||||
|
.apply(record),
|
||||||
);
|
);
|
||||||
|
|
||||||
arg_stack.push(term);
|
arg_stack.push(term);
|
||||||
|
@ -5128,10 +5185,18 @@ impl<'a> CodeGenerator<'a> {
|
||||||
Air::TupleAccessor {
|
Air::TupleAccessor {
|
||||||
tipo,
|
tipo,
|
||||||
names,
|
names,
|
||||||
check_last_item,
|
is_expect,
|
||||||
} => {
|
} => {
|
||||||
let inner_types = tipo.get_inner_types();
|
let inner_types = tipo.get_inner_types();
|
||||||
let value = arg_stack.pop().unwrap();
|
let value = arg_stack.pop().unwrap();
|
||||||
|
|
||||||
|
let error_term = if self.tracing && is_expect && !tipo.is_2_tuple() {
|
||||||
|
let msg = arg_stack.pop().unwrap();
|
||||||
|
Term::Error.delayed_trace(msg)
|
||||||
|
} else {
|
||||||
|
Term::Error
|
||||||
|
};
|
||||||
|
|
||||||
let mut term = arg_stack.pop().unwrap();
|
let mut term = arg_stack.pop().unwrap();
|
||||||
let list_id = self.id_gen.next();
|
let list_id = self.id_gen.next();
|
||||||
|
|
||||||
|
@ -5169,31 +5234,21 @@ impl<'a> CodeGenerator<'a> {
|
||||||
|
|
||||||
let names_types = names.into_iter().zip(inner_types).collect_vec();
|
let names_types = names.into_iter().zip(inner_types).collect_vec();
|
||||||
|
|
||||||
let error_term = if self.tracing {
|
|
||||||
Term::Error.delayed_trace(Term::var(
|
|
||||||
self.special_functions.use_function(TOO_MANY_ITEMS),
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
Term::Error
|
|
||||||
};
|
|
||||||
|
|
||||||
term = builder::list_access_to_uplc(
|
term = builder::list_access_to_uplc(
|
||||||
&names_types,
|
&names_types,
|
||||||
&id_list,
|
&id_list,
|
||||||
false,
|
false,
|
||||||
0,
|
0,
|
||||||
term,
|
term,
|
||||||
check_last_item,
|
is_expect,
|
||||||
false,
|
false,
|
||||||
error_term,
|
error_term,
|
||||||
)
|
)
|
||||||
.apply(value);
|
.apply(value);
|
||||||
|
|
||||||
arg_stack.push(term);
|
arg_stack.push(term);
|
||||||
} else if check_last_item {
|
|
||||||
unreachable!("HOW DID YOU DO THIS");
|
|
||||||
} else {
|
} else {
|
||||||
arg_stack.push(term);
|
unreachable!("HOW DID YOU DO THIS");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Air::Trace { .. } => {
|
Air::Trace { .. } => {
|
||||||
|
|
|
@ -89,11 +89,9 @@ pub enum Air {
|
||||||
},
|
},
|
||||||
AssertConstr {
|
AssertConstr {
|
||||||
constr_index: usize,
|
constr_index: usize,
|
||||||
msg: String,
|
|
||||||
},
|
},
|
||||||
AssertBool {
|
AssertBool {
|
||||||
is_true: bool,
|
is_true: bool,
|
||||||
msg: String,
|
|
||||||
},
|
},
|
||||||
// When
|
// When
|
||||||
When {
|
When {
|
||||||
|
@ -154,14 +152,14 @@ pub enum Air {
|
||||||
// Field Access
|
// Field Access
|
||||||
FieldsExpose {
|
FieldsExpose {
|
||||||
indices: Vec<(usize, String, Rc<Type>)>,
|
indices: Vec<(usize, String, Rc<Type>)>,
|
||||||
check_last_item: bool,
|
is_expect: bool,
|
||||||
},
|
},
|
||||||
// ListAccess
|
// ListAccess
|
||||||
ListAccessor {
|
ListAccessor {
|
||||||
tipo: Rc<Type>,
|
tipo: Rc<Type>,
|
||||||
names: Vec<String>,
|
names: Vec<String>,
|
||||||
tail: bool,
|
tail: bool,
|
||||||
check_last_item: bool,
|
is_expect: bool,
|
||||||
},
|
},
|
||||||
ListExpose {
|
ListExpose {
|
||||||
tipo: Rc<Type>,
|
tipo: Rc<Type>,
|
||||||
|
@ -172,7 +170,7 @@ pub enum Air {
|
||||||
TupleAccessor {
|
TupleAccessor {
|
||||||
names: Vec<String>,
|
names: Vec<String>,
|
||||||
tipo: Rc<Type>,
|
tipo: Rc<Type>,
|
||||||
check_last_item: bool,
|
is_expect: bool,
|
||||||
},
|
},
|
||||||
// Misc.
|
// Misc.
|
||||||
ErrorTerm {
|
ErrorTerm {
|
||||||
|
|
|
@ -18,7 +18,7 @@ use crate::{
|
||||||
AssignmentKind, DataType, Pattern, Span, TypedArg, TypedClause, TypedClauseGuard,
|
AssignmentKind, DataType, Pattern, Span, TypedArg, TypedClause, TypedClauseGuard,
|
||||||
TypedDataType, TypedPattern,
|
TypedDataType, TypedPattern,
|
||||||
},
|
},
|
||||||
builtins::{bool, void},
|
builtins::{bool, data, function, int, list, string, void},
|
||||||
expr::TypedExpr,
|
expr::TypedExpr,
|
||||||
tipo::{PatternConstructor, TypeVar, ValueConstructor, ValueConstructorVariant},
|
tipo::{PatternConstructor, TypeVar, ValueConstructor, ValueConstructorVariant},
|
||||||
};
|
};
|
||||||
|
@ -82,8 +82,7 @@ pub struct AssignmentProperties {
|
||||||
pub kind: AssignmentKind,
|
pub kind: AssignmentKind,
|
||||||
pub remove_unused: bool,
|
pub remove_unused: bool,
|
||||||
pub full_check: bool,
|
pub full_check: bool,
|
||||||
pub location: Span,
|
pub msg_func: AirTree,
|
||||||
pub module_name: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -193,7 +192,7 @@ impl ClauseProperties {
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct CodeGenSpecialFuncs {
|
pub struct CodeGenSpecialFuncs {
|
||||||
pub used_funcs: Vec<String>,
|
pub used_funcs: Vec<String>,
|
||||||
pub key_to_func: IndexMap<String, Term<Name>>,
|
pub key_to_func: IndexMap<String, (Term<Name>, Rc<Type>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CodeGenSpecialFuncs {
|
impl CodeGenSpecialFuncs {
|
||||||
|
@ -202,46 +201,30 @@ impl CodeGenSpecialFuncs {
|
||||||
|
|
||||||
key_to_func.insert(
|
key_to_func.insert(
|
||||||
CONSTR_FIELDS_EXPOSER.to_string(),
|
CONSTR_FIELDS_EXPOSER.to_string(),
|
||||||
Term::snd_pair()
|
(
|
||||||
.apply(Term::unconstr_data().apply(Term::var("__constr_var")))
|
Term::snd_pair()
|
||||||
.lambda("__constr_var"),
|
.apply(Term::unconstr_data().apply(Term::var("__constr_var")))
|
||||||
|
.lambda("__constr_var"),
|
||||||
|
function(vec![data()], list(data())),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
key_to_func.insert(
|
key_to_func.insert(
|
||||||
CONSTR_INDEX_EXPOSER.to_string(),
|
CONSTR_INDEX_EXPOSER.to_string(),
|
||||||
Term::fst_pair()
|
(
|
||||||
.apply(Term::unconstr_data().apply(Term::var("__constr_var")))
|
Term::fst_pair()
|
||||||
.lambda("__constr_var"),
|
.apply(Term::unconstr_data().apply(Term::var("__constr_var")))
|
||||||
|
.lambda("__constr_var"),
|
||||||
|
function(vec![data()], int()),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
key_to_func.insert(
|
key_to_func.insert(
|
||||||
TOO_MANY_ITEMS.to_string(),
|
TOO_MANY_ITEMS.to_string(),
|
||||||
Term::string("List/Tuple/Constr contains more items than expected"),
|
(
|
||||||
);
|
Term::string("List/Tuple/Constr contains more items than expected"),
|
||||||
|
string(),
|
||||||
key_to_func.insert(
|
),
|
||||||
LIST_NOT_EMPTY.to_string(),
|
|
||||||
Term::string("Expected no items for List"),
|
|
||||||
);
|
|
||||||
|
|
||||||
key_to_func.insert(
|
|
||||||
CONSTR_NOT_EMPTY.to_string(),
|
|
||||||
Term::string("Expected no fields for Constr"),
|
|
||||||
);
|
|
||||||
|
|
||||||
key_to_func.insert(
|
|
||||||
INCORRECT_BOOLEAN.to_string(),
|
|
||||||
Term::string("Expected on incorrect Boolean variant"),
|
|
||||||
);
|
|
||||||
|
|
||||||
key_to_func.insert(
|
|
||||||
INCORRECT_CONSTR.to_string(),
|
|
||||||
Term::string("Expected on incorrect Constr variant"),
|
|
||||||
);
|
|
||||||
|
|
||||||
key_to_func.insert(
|
|
||||||
CONSTR_INDEX_MISMATCH.to_string(),
|
|
||||||
Term::string("Constr index didn't match a type variant"),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
CodeGenSpecialFuncs {
|
CodeGenSpecialFuncs {
|
||||||
|
@ -250,15 +233,26 @@ impl CodeGenSpecialFuncs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn use_function(&mut self, func_name: &'static str) -> &'static str {
|
pub fn use_function_tree(&mut self, func_name: String) -> AirTree {
|
||||||
if !self.used_funcs.contains(&func_name.to_string()) {
|
if !self.used_funcs.contains(&func_name) {
|
||||||
self.used_funcs.push(func_name.to_string());
|
self.used_funcs.push(func_name.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let tipo = self.key_to_func.get(&func_name).unwrap().1.clone();
|
||||||
|
|
||||||
|
AirTree::local_var(func_name, tipo)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn use_function_uplc(&mut self, func_name: String) -> String {
|
||||||
|
if !self.used_funcs.contains(&func_name) {
|
||||||
|
self.used_funcs.push(func_name.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
func_name
|
func_name
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_function(&self, func_name: &String) -> Term<Name> {
|
pub fn get_function(&self, func_name: &String) -> Term<Name> {
|
||||||
self.key_to_func[func_name].clone()
|
self.key_to_func[func_name].0.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply_used_functions(&self, mut term: Term<Name>) -> Term<Name> {
|
pub fn apply_used_functions(&self, mut term: Term<Name>) -> Term<Name> {
|
||||||
|
@ -267,6 +261,18 @@ impl CodeGenSpecialFuncs {
|
||||||
}
|
}
|
||||||
term
|
term
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn insert_new_function(
|
||||||
|
&mut self,
|
||||||
|
func_name: String,
|
||||||
|
function: Term<Name>,
|
||||||
|
function_type: Rc<Type>,
|
||||||
|
) {
|
||||||
|
if !self.key_to_func.contains_key(&func_name) {
|
||||||
|
self.key_to_func
|
||||||
|
.insert(func_name, (function, function_type));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for CodeGenSpecialFuncs {
|
impl Default for CodeGenSpecialFuncs {
|
||||||
|
|
|
@ -127,12 +127,12 @@ pub enum AirStatement {
|
||||||
AssertConstr {
|
AssertConstr {
|
||||||
constr_index: usize,
|
constr_index: usize,
|
||||||
constr: Box<AirTree>,
|
constr: Box<AirTree>,
|
||||||
msg: String,
|
msg: Box<AirTree>,
|
||||||
},
|
},
|
||||||
AssertBool {
|
AssertBool {
|
||||||
is_true: bool,
|
is_true: bool,
|
||||||
value: Box<AirTree>,
|
value: Box<AirTree>,
|
||||||
msg: String,
|
msg: Box<AirTree>,
|
||||||
},
|
},
|
||||||
// Clause Guards
|
// Clause Guards
|
||||||
ClauseGuard {
|
ClauseGuard {
|
||||||
|
@ -154,16 +154,16 @@ pub enum AirStatement {
|
||||||
// Field Access
|
// Field Access
|
||||||
FieldsExpose {
|
FieldsExpose {
|
||||||
indices: Vec<(usize, String, Rc<Type>)>,
|
indices: Vec<(usize, String, Rc<Type>)>,
|
||||||
check_last_item: bool,
|
|
||||||
record: Box<AirTree>,
|
record: Box<AirTree>,
|
||||||
|
msg: Box<Option<AirTree>>,
|
||||||
},
|
},
|
||||||
// List Access
|
// List Access
|
||||||
ListAccessor {
|
ListAccessor {
|
||||||
tipo: Rc<Type>,
|
tipo: Rc<Type>,
|
||||||
names: Vec<String>,
|
names: Vec<String>,
|
||||||
tail: bool,
|
tail: bool,
|
||||||
check_last_item: bool,
|
|
||||||
list: Box<AirTree>,
|
list: Box<AirTree>,
|
||||||
|
msg: Box<Option<AirTree>>,
|
||||||
},
|
},
|
||||||
ListExpose {
|
ListExpose {
|
||||||
tipo: Rc<Type>,
|
tipo: Rc<Type>,
|
||||||
|
@ -174,15 +174,17 @@ pub enum AirStatement {
|
||||||
TupleAccessor {
|
TupleAccessor {
|
||||||
names: Vec<String>,
|
names: Vec<String>,
|
||||||
tipo: Rc<Type>,
|
tipo: Rc<Type>,
|
||||||
check_last_item: bool,
|
|
||||||
tuple: Box<AirTree>,
|
tuple: Box<AirTree>,
|
||||||
|
msg: Box<Option<AirTree>>,
|
||||||
},
|
},
|
||||||
// Misc.
|
// Misc.
|
||||||
FieldsEmpty {
|
FieldsEmpty {
|
||||||
constr: Box<AirTree>,
|
constr: Box<AirTree>,
|
||||||
|
msg: Box<AirTree>,
|
||||||
},
|
},
|
||||||
ListEmpty {
|
ListEmpty {
|
||||||
list: Box<AirTree>,
|
list: Box<AirTree>,
|
||||||
|
msg: Box<AirTree>,
|
||||||
},
|
},
|
||||||
NoOp,
|
NoOp,
|
||||||
}
|
}
|
||||||
|
@ -494,22 +496,22 @@ impl AirTree {
|
||||||
value: value.into(),
|
value: value.into(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub fn assert_constr_index(constr_index: usize, constr: AirTree, msg: String) -> AirTree {
|
pub fn assert_constr_index(constr_index: usize, constr: AirTree, msg: AirTree) -> AirTree {
|
||||||
AirTree::Statement {
|
AirTree::Statement {
|
||||||
statement: AirStatement::AssertConstr {
|
statement: AirStatement::AssertConstr {
|
||||||
constr_index,
|
constr_index,
|
||||||
constr: constr.into(),
|
constr: constr.into(),
|
||||||
msg,
|
msg: msg.into(),
|
||||||
},
|
},
|
||||||
hoisted_over: None,
|
hoisted_over: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn assert_bool(is_true: bool, value: AirTree, msg: String) -> AirTree {
|
pub fn assert_bool(is_true: bool, value: AirTree, msg: AirTree) -> AirTree {
|
||||||
AirTree::Statement {
|
AirTree::Statement {
|
||||||
statement: AirStatement::AssertBool {
|
statement: AirStatement::AssertBool {
|
||||||
is_true,
|
is_true,
|
||||||
value: value.into(),
|
value: value.into(),
|
||||||
msg,
|
msg: msg.into(),
|
||||||
},
|
},
|
||||||
hoisted_over: None,
|
hoisted_over: None,
|
||||||
}
|
}
|
||||||
|
@ -714,14 +716,14 @@ impl AirTree {
|
||||||
|
|
||||||
pub fn fields_expose(
|
pub fn fields_expose(
|
||||||
indices: Vec<(usize, String, Rc<Type>)>,
|
indices: Vec<(usize, String, Rc<Type>)>,
|
||||||
check_last_item: bool,
|
|
||||||
record: AirTree,
|
record: AirTree,
|
||||||
|
msg: Option<AirTree>,
|
||||||
) -> AirTree {
|
) -> AirTree {
|
||||||
AirTree::Statement {
|
AirTree::Statement {
|
||||||
statement: AirStatement::FieldsExpose {
|
statement: AirStatement::FieldsExpose {
|
||||||
indices,
|
indices,
|
||||||
check_last_item,
|
|
||||||
record: record.into(),
|
record: record.into(),
|
||||||
|
msg: msg.into(),
|
||||||
},
|
},
|
||||||
hoisted_over: None,
|
hoisted_over: None,
|
||||||
}
|
}
|
||||||
|
@ -730,16 +732,16 @@ impl AirTree {
|
||||||
names: Vec<String>,
|
names: Vec<String>,
|
||||||
tipo: Rc<Type>,
|
tipo: Rc<Type>,
|
||||||
tail: bool,
|
tail: bool,
|
||||||
check_last_item: bool,
|
|
||||||
list: AirTree,
|
list: AirTree,
|
||||||
|
msg: Option<AirTree>,
|
||||||
) -> AirTree {
|
) -> AirTree {
|
||||||
AirTree::Statement {
|
AirTree::Statement {
|
||||||
statement: AirStatement::ListAccessor {
|
statement: AirStatement::ListAccessor {
|
||||||
tipo,
|
tipo,
|
||||||
names,
|
names,
|
||||||
tail,
|
tail,
|
||||||
check_last_item,
|
|
||||||
list: list.into(),
|
list: list.into(),
|
||||||
|
msg: msg.into(),
|
||||||
},
|
},
|
||||||
hoisted_over: None,
|
hoisted_over: None,
|
||||||
}
|
}
|
||||||
|
@ -761,15 +763,15 @@ impl AirTree {
|
||||||
pub fn tuple_access(
|
pub fn tuple_access(
|
||||||
names: Vec<String>,
|
names: Vec<String>,
|
||||||
tipo: Rc<Type>,
|
tipo: Rc<Type>,
|
||||||
check_last_item: bool,
|
|
||||||
tuple: AirTree,
|
tuple: AirTree,
|
||||||
|
msg: Option<AirTree>,
|
||||||
) -> AirTree {
|
) -> AirTree {
|
||||||
AirTree::Statement {
|
AirTree::Statement {
|
||||||
statement: AirStatement::TupleAccessor {
|
statement: AirStatement::TupleAccessor {
|
||||||
names,
|
names,
|
||||||
tipo,
|
tipo,
|
||||||
check_last_item,
|
|
||||||
tuple: tuple.into(),
|
tuple: tuple.into(),
|
||||||
|
msg: msg.into(),
|
||||||
},
|
},
|
||||||
hoisted_over: None,
|
hoisted_over: None,
|
||||||
}
|
}
|
||||||
|
@ -804,17 +806,21 @@ impl AirTree {
|
||||||
hoisted_over: None,
|
hoisted_over: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn fields_empty(constr: AirTree) -> AirTree {
|
pub fn fields_empty(constr: AirTree, msg: AirTree) -> AirTree {
|
||||||
AirTree::Statement {
|
AirTree::Statement {
|
||||||
statement: AirStatement::FieldsEmpty {
|
statement: AirStatement::FieldsEmpty {
|
||||||
constr: constr.into(),
|
constr: constr.into(),
|
||||||
|
msg: msg.into(),
|
||||||
},
|
},
|
||||||
hoisted_over: None,
|
hoisted_over: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn list_empty(list: AirTree) -> AirTree {
|
pub fn list_empty(list: AirTree, msg: AirTree) -> AirTree {
|
||||||
AirTree::Statement {
|
AirTree::Statement {
|
||||||
statement: AirStatement::ListEmpty { list: list.into() },
|
statement: AirStatement::ListEmpty {
|
||||||
|
list: list.into(),
|
||||||
|
msg: msg.into(),
|
||||||
|
},
|
||||||
hoisted_over: None,
|
hoisted_over: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -952,20 +958,18 @@ impl AirTree {
|
||||||
} => {
|
} => {
|
||||||
air_vec.push(Air::AssertConstr {
|
air_vec.push(Air::AssertConstr {
|
||||||
constr_index: *constr_index,
|
constr_index: *constr_index,
|
||||||
msg: msg.clone(),
|
|
||||||
});
|
});
|
||||||
constr.create_air_vec(air_vec);
|
constr.create_air_vec(air_vec);
|
||||||
|
msg.create_air_vec(air_vec);
|
||||||
}
|
}
|
||||||
AirStatement::AssertBool {
|
AirStatement::AssertBool {
|
||||||
is_true,
|
is_true,
|
||||||
value,
|
value,
|
||||||
msg,
|
msg,
|
||||||
} => {
|
} => {
|
||||||
air_vec.push(Air::AssertBool {
|
air_vec.push(Air::AssertBool { is_true: *is_true });
|
||||||
is_true: *is_true,
|
|
||||||
msg: msg.clone(),
|
|
||||||
});
|
|
||||||
value.create_air_vec(air_vec);
|
value.create_air_vec(air_vec);
|
||||||
|
msg.create_air_vec(air_vec);
|
||||||
}
|
}
|
||||||
AirStatement::ClauseGuard {
|
AirStatement::ClauseGuard {
|
||||||
subject_name,
|
subject_name,
|
||||||
|
@ -1005,29 +1009,35 @@ impl AirTree {
|
||||||
}
|
}
|
||||||
AirStatement::FieldsExpose {
|
AirStatement::FieldsExpose {
|
||||||
indices,
|
indices,
|
||||||
check_last_item,
|
|
||||||
record,
|
record,
|
||||||
|
msg,
|
||||||
} => {
|
} => {
|
||||||
air_vec.push(Air::FieldsExpose {
|
air_vec.push(Air::FieldsExpose {
|
||||||
indices: indices.clone(),
|
indices: indices.clone(),
|
||||||
check_last_item: *check_last_item,
|
is_expect: msg.is_some(),
|
||||||
});
|
});
|
||||||
record.create_air_vec(air_vec);
|
record.create_air_vec(air_vec);
|
||||||
|
msg.iter().for_each(|msg| {
|
||||||
|
msg.create_air_vec(air_vec);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
AirStatement::ListAccessor {
|
AirStatement::ListAccessor {
|
||||||
tipo,
|
tipo,
|
||||||
names,
|
names,
|
||||||
tail,
|
tail,
|
||||||
check_last_item,
|
|
||||||
list,
|
list,
|
||||||
|
msg,
|
||||||
} => {
|
} => {
|
||||||
air_vec.push(Air::ListAccessor {
|
air_vec.push(Air::ListAccessor {
|
||||||
tipo: tipo.clone(),
|
tipo: tipo.clone(),
|
||||||
names: names.clone(),
|
names: names.clone(),
|
||||||
tail: *tail,
|
tail: *tail,
|
||||||
check_last_item: *check_last_item,
|
is_expect: msg.is_some(),
|
||||||
});
|
});
|
||||||
list.create_air_vec(air_vec);
|
list.create_air_vec(air_vec);
|
||||||
|
msg.iter().for_each(|msg| {
|
||||||
|
msg.create_air_vec(air_vec);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
AirStatement::ListExpose {
|
AirStatement::ListExpose {
|
||||||
tipo,
|
tipo,
|
||||||
|
@ -1043,26 +1053,31 @@ impl AirTree {
|
||||||
AirStatement::TupleAccessor {
|
AirStatement::TupleAccessor {
|
||||||
names,
|
names,
|
||||||
tipo,
|
tipo,
|
||||||
check_last_item,
|
|
||||||
tuple,
|
tuple,
|
||||||
|
msg,
|
||||||
} => {
|
} => {
|
||||||
air_vec.push(Air::TupleAccessor {
|
air_vec.push(Air::TupleAccessor {
|
||||||
names: names.clone(),
|
names: names.clone(),
|
||||||
tipo: tipo.clone(),
|
tipo: tipo.clone(),
|
||||||
check_last_item: *check_last_item,
|
is_expect: msg.is_some(),
|
||||||
});
|
});
|
||||||
tuple.create_air_vec(air_vec);
|
tuple.create_air_vec(air_vec);
|
||||||
|
msg.iter().for_each(|msg| {
|
||||||
|
msg.create_air_vec(air_vec);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
AirStatement::NoOp => {
|
AirStatement::NoOp => {
|
||||||
air_vec.push(Air::NoOp);
|
air_vec.push(Air::NoOp);
|
||||||
}
|
}
|
||||||
AirStatement::FieldsEmpty { constr } => {
|
AirStatement::FieldsEmpty { constr, msg } => {
|
||||||
air_vec.push(Air::FieldsEmpty);
|
air_vec.push(Air::FieldsEmpty);
|
||||||
constr.create_air_vec(air_vec);
|
constr.create_air_vec(air_vec);
|
||||||
|
msg.create_air_vec(air_vec);
|
||||||
}
|
}
|
||||||
AirStatement::ListEmpty { list } => {
|
AirStatement::ListEmpty { list, msg } => {
|
||||||
air_vec.push(Air::ListEmpty);
|
air_vec.push(Air::ListEmpty);
|
||||||
list.create_air_vec(air_vec);
|
list.create_air_vec(air_vec);
|
||||||
|
msg.create_air_vec(air_vec);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
exp.create_air_vec(air_vec);
|
exp.create_air_vec(air_vec);
|
||||||
|
@ -1527,7 +1542,7 @@ impl AirTree {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
AirStatement::NoOp => {}
|
AirStatement::NoOp => {}
|
||||||
AirStatement::FieldsEmpty { constr } => {
|
AirStatement::FieldsEmpty { constr, .. } => {
|
||||||
constr.do_traverse_tree_with(
|
constr.do_traverse_tree_with(
|
||||||
tree_path,
|
tree_path,
|
||||||
current_depth + 1,
|
current_depth + 1,
|
||||||
|
@ -1536,7 +1551,7 @@ impl AirTree {
|
||||||
apply_with_func_last,
|
apply_with_func_last,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
AirStatement::ListEmpty { list } => {
|
AirStatement::ListEmpty { list, .. } => {
|
||||||
list.do_traverse_tree_with(
|
list.do_traverse_tree_with(
|
||||||
tree_path,
|
tree_path,
|
||||||
current_depth + 1,
|
current_depth + 1,
|
||||||
|
@ -1997,7 +2012,7 @@ impl AirTree {
|
||||||
}
|
}
|
||||||
AirStatement::DefineFunc { .. } => unreachable!(),
|
AirStatement::DefineFunc { .. } => unreachable!(),
|
||||||
AirStatement::DefineCyclicFuncs { .. } => unreachable!(),
|
AirStatement::DefineCyclicFuncs { .. } => unreachable!(),
|
||||||
AirStatement::FieldsEmpty { constr } => {
|
AirStatement::FieldsEmpty { constr, .. } => {
|
||||||
if *index == 0 {
|
if *index == 0 {
|
||||||
constr.as_mut().do_find_air_tree_node(tree_path_iter)
|
constr.as_mut().do_find_air_tree_node(tree_path_iter)
|
||||||
} else if *index == 1 {
|
} else if *index == 1 {
|
||||||
|
@ -2006,7 +2021,7 @@ impl AirTree {
|
||||||
panic!("Tree Path index outside tree children nodes")
|
panic!("Tree Path index outside tree children nodes")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AirStatement::ListEmpty { list } => {
|
AirStatement::ListEmpty { list, .. } => {
|
||||||
if *index == 0 {
|
if *index == 0 {
|
||||||
list.as_mut().do_find_air_tree_node(tree_path_iter)
|
list.as_mut().do_find_air_tree_node(tree_path_iter)
|
||||||
} else if *index == 1 {
|
} else if *index == 1 {
|
||||||
|
|
Loading…
Reference in New Issue