Add 'diagnostic' to the prelude, as well as companion functions.
This is not fully satisfactory as it pollutes a bit the prelude. Ideally, those functions should only be visible and usable by the underlying trace code. But for now, we'll just go with it.
This commit is contained in:
parent
754ed07408
commit
beb5ac4643
|
@ -1,4 +1,5 @@
|
|||
use crate::{
|
||||
aiken_fn,
|
||||
ast::{
|
||||
Annotation, ArgName, CallArg, DataTypeKey, Function, FunctionAccessKey, ModuleKind,
|
||||
OnTestFailure, Span, TypedArg, TypedDataType, TypedFunction, UnOp,
|
||||
|
@ -268,6 +269,102 @@ pub fn prelude(id_gen: &IdGenerator) -> TypeInfo {
|
|||
),
|
||||
);
|
||||
|
||||
// enumerate
|
||||
let enumerate_a = generic_var(id_gen.next());
|
||||
let enumerate_b = generic_var(id_gen.next());
|
||||
prelude.values.insert(
|
||||
"enumerate".to_string(),
|
||||
ValueConstructor::public(
|
||||
function(
|
||||
vec![
|
||||
list(enumerate_a.clone()),
|
||||
enumerate_b.clone(),
|
||||
function(
|
||||
vec![enumerate_a.clone(), enumerate_b.clone()],
|
||||
enumerate_b.clone(),
|
||||
),
|
||||
function(
|
||||
vec![enumerate_a.clone(), enumerate_b.clone()],
|
||||
enumerate_b.clone(),
|
||||
),
|
||||
],
|
||||
enumerate_b,
|
||||
),
|
||||
ValueConstructorVariant::ModuleFn {
|
||||
name: "enumerate".to_string(),
|
||||
field_map: None,
|
||||
module: "".to_string(),
|
||||
arity: 4,
|
||||
location: Span::empty(),
|
||||
builtin: None,
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// encode_base16
|
||||
prelude.values.insert(
|
||||
"encode_base16".to_string(),
|
||||
ValueConstructor::public(
|
||||
function(vec![byte_array(), int(), byte_array()], byte_array()),
|
||||
ValueConstructorVariant::ModuleFn {
|
||||
name: "encode_base16".to_string(),
|
||||
field_map: None,
|
||||
module: "".to_string(),
|
||||
arity: 3,
|
||||
location: Span::empty(),
|
||||
builtin: None,
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// from_int
|
||||
prelude.values.insert(
|
||||
"from_int".to_string(),
|
||||
ValueConstructor::public(
|
||||
function(vec![int(), byte_array()], byte_array()),
|
||||
ValueConstructorVariant::ModuleFn {
|
||||
name: "from_int".to_string(),
|
||||
field_map: None,
|
||||
module: "".to_string(),
|
||||
arity: 2,
|
||||
location: Span::empty(),
|
||||
builtin: None,
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// do_from_int
|
||||
prelude.values.insert(
|
||||
"do_from_int".to_string(),
|
||||
ValueConstructor::public(
|
||||
function(vec![int(), byte_array()], byte_array()),
|
||||
ValueConstructorVariant::ModuleFn {
|
||||
name: "do_from_int".to_string(),
|
||||
field_map: None,
|
||||
module: "".to_string(),
|
||||
arity: 2,
|
||||
location: Span::empty(),
|
||||
builtin: None,
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// diagnostic
|
||||
prelude.values.insert(
|
||||
"diagnostic".to_string(),
|
||||
ValueConstructor::public(
|
||||
function(vec![data(), byte_array()], byte_array()),
|
||||
ValueConstructorVariant::ModuleFn {
|
||||
name: "diagnostic".to_string(),
|
||||
field_map: None,
|
||||
module: "".to_string(),
|
||||
arity: 2,
|
||||
location: Span::empty(),
|
||||
builtin: None,
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// always
|
||||
let always_a_var = generic_var(id_gen.next());
|
||||
let always_b_var = generic_var(id_gen.next());
|
||||
|
@ -919,7 +1016,10 @@ pub fn from_default_function(builtin: DefaultFunction, id_gen: &IdGenerator) ->
|
|||
)
|
||||
}
|
||||
|
||||
pub fn prelude_functions(id_gen: &IdGenerator) -> IndexMap<FunctionAccessKey, TypedFunction> {
|
||||
pub fn prelude_functions(
|
||||
id_gen: &IdGenerator,
|
||||
module_types: &HashMap<String, TypeInfo>,
|
||||
) -> IndexMap<FunctionAccessKey, TypedFunction> {
|
||||
let mut functions = IndexMap::new();
|
||||
|
||||
// /// Negate the argument. Useful for map/fold and pipelines.
|
||||
|
@ -1239,6 +1339,230 @@ pub fn prelude_functions(id_gen: &IdGenerator) -> IndexMap<FunctionAccessKey, Ty
|
|||
},
|
||||
);
|
||||
|
||||
functions.insert(
|
||||
FunctionAccessKey {
|
||||
module_name: "".to_string(),
|
||||
function_name: "enumerate".to_string(),
|
||||
},
|
||||
aiken_fn!(
|
||||
&module_types,
|
||||
&id_gen,
|
||||
r#"
|
||||
fn enumerate(
|
||||
self: List<a>,
|
||||
zero: b,
|
||||
with: fn(a, b) -> b,
|
||||
last: fn(a, b) -> b,
|
||||
) -> b {
|
||||
when self is {
|
||||
[] -> zero
|
||||
[x] -> last(x, zero)
|
||||
[x, ..xs] -> with(x, enumerate(xs, zero, with, last))
|
||||
}
|
||||
}
|
||||
"#
|
||||
),
|
||||
);
|
||||
|
||||
functions.insert(
|
||||
FunctionAccessKey {
|
||||
module_name: "".to_string(),
|
||||
function_name: "encode_base16".to_string(),
|
||||
},
|
||||
aiken_fn!(
|
||||
&module_types,
|
||||
&id_gen,
|
||||
r#"
|
||||
use aiken/builtin
|
||||
|
||||
fn encode_base16(bytes: ByteArray, ix: Int, builder: ByteArray) -> ByteArray {
|
||||
if ix < 0 {
|
||||
builder
|
||||
} else {
|
||||
let byte = builtin.index_bytearray(bytes, ix)
|
||||
let msb = byte / 16
|
||||
let lsb = byte % 16
|
||||
let builder =
|
||||
builtin.cons_bytearray(
|
||||
msb + if msb < 10 {
|
||||
48
|
||||
} else {
|
||||
55
|
||||
},
|
||||
builtin.cons_bytearray(
|
||||
lsb + if lsb < 10 {
|
||||
48
|
||||
} else {
|
||||
55
|
||||
},
|
||||
builder,
|
||||
),
|
||||
)
|
||||
encode_base16(bytes, ix - 1, builder)
|
||||
}
|
||||
}
|
||||
"#
|
||||
),
|
||||
);
|
||||
|
||||
functions.insert(
|
||||
FunctionAccessKey {
|
||||
module_name: "".to_string(),
|
||||
function_name: "do_from_int".to_string(),
|
||||
},
|
||||
aiken_fn!(
|
||||
&module_types,
|
||||
&id_gen,
|
||||
r#"
|
||||
use aiken/builtin
|
||||
|
||||
fn do_from_int(i: Int, digits: ByteArray) -> ByteArray {
|
||||
if i <= 0 {
|
||||
digits
|
||||
} else {
|
||||
do_from_int(
|
||||
builtin.quotient_integer(i, 10),
|
||||
builtin.cons_bytearray(builtin.remainder_integer(i, 10) + 48, digits),
|
||||
)
|
||||
}
|
||||
}
|
||||
"#
|
||||
),
|
||||
);
|
||||
|
||||
functions.insert(
|
||||
FunctionAccessKey {
|
||||
module_name: "".to_string(),
|
||||
function_name: "from_int".to_string(),
|
||||
},
|
||||
aiken_fn!(
|
||||
&module_types,
|
||||
&id_gen,
|
||||
r#"
|
||||
use aiken/builtin
|
||||
|
||||
/// Encode an integer into UTF-8.
|
||||
fn from_int(i: Int, digits: ByteArray) -> ByteArray {
|
||||
if i == 0 {
|
||||
builtin.append_bytearray(#"30", digits)
|
||||
} else if i < 0 {
|
||||
builtin.append_bytearray(#"2d", from_int(-i, digits))
|
||||
} else {
|
||||
do_from_int(
|
||||
builtin.quotient_integer(i, 10),
|
||||
builtin.cons_bytearray(builtin.remainder_integer(i, 10) + 48, digits),
|
||||
)
|
||||
}
|
||||
}
|
||||
"#
|
||||
),
|
||||
);
|
||||
|
||||
functions.insert(
|
||||
FunctionAccessKey {
|
||||
module_name: "".to_string(),
|
||||
function_name: "diagnostic".to_string(),
|
||||
},
|
||||
aiken_fn!(
|
||||
&module_types,
|
||||
&id_gen,
|
||||
r#"
|
||||
use aiken/builtin
|
||||
|
||||
fn diagnostic(self: Data, builder: ByteArray) -> ByteArray {
|
||||
builtin.choose_data(
|
||||
self,
|
||||
{
|
||||
let Pair(constr, fields) = builtin.un_constr_data(self)
|
||||
|
||||
let builder =
|
||||
when fields is {
|
||||
[] -> builtin.append_bytearray(#"5b5d29", builder)
|
||||
_ -> {
|
||||
let bytes =
|
||||
enumerate(
|
||||
fields,
|
||||
builtin.append_bytearray(#"5d29", builder),
|
||||
fn(e: Data, st: ByteArray) {
|
||||
diagnostic(e, builtin.append_bytearray(#"2c20", st))
|
||||
},
|
||||
fn(e: Data, st: ByteArray) { diagnostic(e, st) },
|
||||
)
|
||||
builtin.append_bytearray(#"5b5f20", bytes)
|
||||
}
|
||||
}
|
||||
|
||||
let constr_tag =
|
||||
if constr < 7 {
|
||||
121 + constr
|
||||
} else if constr < 128 {
|
||||
1280 + constr - 7
|
||||
} else {
|
||||
fail @"What are you doing? No I mean, seriously."
|
||||
}
|
||||
|
||||
builder
|
||||
|> builtin.append_bytearray(#"28", _)
|
||||
|> from_int(constr_tag, _)
|
||||
},
|
||||
{
|
||||
let elems = builtin.un_map_data(self)
|
||||
when elems is {
|
||||
[] -> builtin.append_bytearray(#"7b7d", builder)
|
||||
_ -> {
|
||||
let bytes =
|
||||
enumerate(
|
||||
elems,
|
||||
builtin.append_bytearray(#"207d", builder),
|
||||
fn(e: Pair<Data, Data>, st: ByteArray) {
|
||||
let value = diagnostic(e.2nd, builtin.append_bytearray(#"2c20", st))
|
||||
diagnostic(e.1st, builtin.append_bytearray(#"3a20", value))
|
||||
},
|
||||
fn(e: Pair<Data, Data>, st: ByteArray) {
|
||||
let value = diagnostic(e.2nd, st)
|
||||
diagnostic(e.1st, builtin.append_bytearray(#"3a20", value))
|
||||
},
|
||||
)
|
||||
builtin.append_bytearray(#"7b5f20", bytes)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
let elems = builtin.un_list_data(self)
|
||||
when elems is {
|
||||
[] -> builtin.append_bytearray(#"5b5d", builder)
|
||||
_ -> {
|
||||
let bytes =
|
||||
enumerate(
|
||||
elems,
|
||||
builtin.append_bytearray(#"5d", builder),
|
||||
fn(e: Data, st: ByteArray) {
|
||||
diagnostic(e, builtin.append_bytearray(#"2c20", st))
|
||||
},
|
||||
fn(e: Data, st: ByteArray) { diagnostic(e, st) },
|
||||
)
|
||||
builtin.append_bytearray(#"5b5f20", bytes)
|
||||
}
|
||||
}
|
||||
},
|
||||
self
|
||||
|> builtin.un_i_data
|
||||
|> from_int(builder),
|
||||
{
|
||||
let bytes = builtin.un_b_data(self)
|
||||
bytes
|
||||
|> encode_base16(
|
||||
builtin.length_of_bytearray(bytes) - 1,
|
||||
builtin.append_bytearray(#"27", builder),
|
||||
)
|
||||
|> builtin.append_bytearray(#"6827", _)
|
||||
},
|
||||
)
|
||||
}
|
||||
"#
|
||||
),
|
||||
);
|
||||
|
||||
functions
|
||||
}
|
||||
|
||||
|
|
|
@ -31,5 +31,36 @@ impl IdGenerator {
|
|||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! aiken_fn {
|
||||
($module_types:expr, $id_gen:expr, $src:expr) => {{
|
||||
let (untyped_module, _) = $crate::parser::module($src, $crate::ast::ModuleKind::Lib)
|
||||
.expect("failed to parse module.");
|
||||
|
||||
let module_name = "";
|
||||
|
||||
let mut warnings = vec![];
|
||||
|
||||
let typed_module = untyped_module
|
||||
.infer(
|
||||
$id_gen,
|
||||
$crate::ast::ModuleKind::Lib,
|
||||
module_name,
|
||||
$module_types,
|
||||
$crate::ast::Tracing::silent(),
|
||||
&mut warnings,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
if let Some($crate::ast::Definition::Fn(typed_fn)) =
|
||||
typed_module.definitions.into_iter().last()
|
||||
{
|
||||
typed_fn
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
|
|
@ -12,10 +12,10 @@ use itertools::Itertools;
|
|||
use std::{cell::RefCell, collections::HashMap, ops::Deref, rc::Rc};
|
||||
use uplc::{ast::Type as UplcType, builtins::DefaultFunction};
|
||||
|
||||
mod environment;
|
||||
pub(crate) mod environment;
|
||||
pub mod error;
|
||||
mod exhaustive;
|
||||
mod expr;
|
||||
pub(crate) mod expr;
|
||||
pub mod fields;
|
||||
mod hydrator;
|
||||
mod infer;
|
||||
|
|
|
@ -96,7 +96,7 @@ pub(crate) fn infer_function(
|
|||
|
||||
let preregistered_fn = environment
|
||||
.get_variable(name)
|
||||
.expect("Could not find preregistered type for function");
|
||||
.unwrap_or_else(|| panic!("Could not find preregistered type for function: {name}"));
|
||||
|
||||
let field_map = preregistered_fn.field_map().cloned();
|
||||
|
||||
|
@ -1797,7 +1797,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
})
|
||||
}
|
||||
|
||||
fn infer_fn(
|
||||
pub fn infer_fn(
|
||||
&mut self,
|
||||
args: Vec<UntypedArg>,
|
||||
expected_args: &[Rc<Type>],
|
||||
|
@ -2435,7 +2435,10 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
tipo: string(),
|
||||
value: if ix == 0 { ": " } else { ", " }.to_string(),
|
||||
};
|
||||
typed_arguments.into_iter().enumerate().fold(label, |text, (ix, arg)| {
|
||||
typed_arguments
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.fold(label, |text, (ix, arg)| {
|
||||
append_string_expr(append_string_expr(text, delimiter(ix)), arg)
|
||||
})
|
||||
};
|
||||
|
@ -2813,7 +2816,8 @@ pub fn ensure_serialisable(is_top_level: bool, t: Rc<Type>, location: Span) -> R
|
|||
}
|
||||
|
||||
pub fn append_string_expr(left: TypedExpr, right: TypedExpr) -> TypedExpr {
|
||||
let value_constructor = from_default_function(DefaultFunction::AppendString, &IdGenerator::new());
|
||||
let value_constructor =
|
||||
from_default_function(DefaultFunction::AppendString, &IdGenerator::new());
|
||||
let append_string = TypedExpr::ModuleSelect {
|
||||
location: Span::empty(),
|
||||
tipo: value_constructor.tipo,
|
||||
|
@ -2827,9 +2831,7 @@ pub fn append_string_expr(left: TypedExpr, right: TypedExpr) -> TypedExpr {
|
|||
// So this is merely a small work-around for convenience. The proper way here would be to
|
||||
// pull the function definition for append_string from the pre-registered builtins
|
||||
// functions somewhere in the environment.
|
||||
constructor: value_constructor
|
||||
.variant
|
||||
.to_module_value_constructor(
|
||||
constructor: value_constructor.variant.to_module_value_constructor(
|
||||
string(),
|
||||
BUILTIN,
|
||||
&DefaultFunction::AppendString.aiken_name(),
|
||||
|
|
|
@ -9,7 +9,7 @@ Schema {
|
|||
Var {
|
||||
tipo: RefCell {
|
||||
value: Generic {
|
||||
id: 35,
|
||||
id: 64,
|
||||
},
|
||||
},
|
||||
alias: None,
|
||||
|
|
|
@ -130,7 +130,7 @@ where
|
|||
module_types.insert("aiken".to_string(), builtins::prelude(&id_gen));
|
||||
module_types.insert("aiken/builtin".to_string(), builtins::plutus(&id_gen));
|
||||
|
||||
let functions = builtins::prelude_functions(&id_gen);
|
||||
let functions = builtins::prelude_functions(&id_gen, &module_types);
|
||||
|
||||
let data_types = builtins::prelude_data_types(&id_gen);
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ Schema {
|
|||
Var {
|
||||
tipo: RefCell {
|
||||
value: Generic {
|
||||
id: 35,
|
||||
id: 64,
|
||||
},
|
||||
},
|
||||
alias: None,
|
||||
|
|
|
@ -1328,7 +1328,7 @@ mod test {
|
|||
.last()
|
||||
.expect("No test found in declared src?");
|
||||
|
||||
let mut functions = builtins::prelude_functions(&id_gen);
|
||||
let mut functions = builtins::prelude_functions(&id_gen, &module_types);
|
||||
let mut data_types = builtins::prelude_data_types(&id_gen);
|
||||
ast.register_definitions(&mut functions, &mut data_types);
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ impl TestProject {
|
|||
module_types.insert("aiken".to_string(), builtins::prelude(&id_gen));
|
||||
module_types.insert("aiken/builtin".to_string(), builtins::plutus(&id_gen));
|
||||
|
||||
let functions = builtins::prelude_functions(&id_gen);
|
||||
let functions = builtins::prelude_functions(&id_gen, &module_types);
|
||||
let data_types = builtins::prelude_data_types(&id_gen);
|
||||
|
||||
TestProject {
|
||||
|
|
Loading…
Reference in New Issue