use crate::{ aiken_fn, ast::{ well_known, Annotation, ArgName, CallArg, DataType, DataTypeKey, Function, FunctionAccessKey, ModuleKind, OnTestFailure, RecordConstructor, RecordConstructorArg, Span, TypedArg, TypedDataType, TypedFunction, UnOp, }, expr::TypedExpr, tipo::{ fields::FieldMap, Type, TypeConstructor, TypeInfo, ValueConstructor, ValueConstructorVariant, }, IdGenerator, }; use indexmap::IndexMap; use std::{collections::HashMap, rc::Rc}; use strum::IntoEnumIterator; use uplc::builtins::DefaultFunction; pub const PRELUDE: &str = "aiken"; pub const BUILTIN: &str = "aiken/builtin"; /// Build a prelude that can be injected /// into a compiler pipeline pub fn prelude(id_gen: &IdGenerator) -> TypeInfo { let mut prelude = TypeInfo { name: PRELUDE.to_string(), package: "".to_string(), kind: ModuleKind::Lib, types: HashMap::new(), types_constructors: HashMap::new(), values: HashMap::new(), accessors: HashMap::new(), annotations: HashMap::new(), }; // Data prelude.types.insert( well_known::DATA.to_string(), TypeConstructor::primitive(Type::data()), ); // Int prelude.types.insert( well_known::INT.to_string(), TypeConstructor::primitive(Type::int()), ); // ByteArray prelude.types.insert( well_known::BYTE_ARRAY.to_string(), TypeConstructor::primitive(Type::byte_array()), ); // Bool prelude.types.insert( well_known::BOOL.to_string(), TypeConstructor::primitive(Type::bool()), ); prelude.types_constructors.insert( well_known::BOOL.to_string(), ValueConstructor::known_enum( &mut prelude.values, Type::bool(), well_known::BOOL_CONSTRUCTORS, ), ); // G1Element prelude.types.insert( well_known::G1_ELEMENT.to_string(), TypeConstructor::primitive(Type::g1_element()), ); // G2Element prelude.types.insert( well_known::G2_ELEMENT.to_string(), TypeConstructor::primitive(Type::g2_element()), ); // MillerLoopResult prelude.types.insert( well_known::MILLER_LOOP_RESULT.to_string(), TypeConstructor::primitive(Type::miller_loop_result()), ); // Ordering prelude.types.insert( well_known::ORDERING.to_string(), TypeConstructor::primitive(Type::ordering()), ); prelude.types_constructors.insert( well_known::ORDERING.to_string(), ValueConstructor::known_enum( &mut prelude.values, Type::ordering(), well_known::ORDERING_CONSTRUCTORS, ), ); // String prelude.types.insert( well_known::STRING.to_string(), TypeConstructor::primitive(Type::string()), ); // Void prelude.types.insert( well_known::VOID.to_string(), TypeConstructor::primitive(Type::void()), ); prelude.types_constructors.insert( well_known::VOID.to_string(), ValueConstructor::known_enum( &mut prelude.values, Type::void(), well_known::VOID_CONSTRUCTORS, ), ); // List(a) prelude.types.insert( well_known::LIST.to_string(), TypeConstructor::primitive(Type::list(Type::generic_var(id_gen.next()))), ); // Pair(a, b) prelude.types.insert( well_known::PAIR.to_string(), TypeConstructor::primitive(Type::pair( Type::generic_var(id_gen.next()), Type::generic_var(id_gen.next()), )), ); prelude.types_constructors.insert( well_known::PAIR.to_string(), vec![well_known::PAIR.to_string()], ); // Pairs = List> prelude.types.insert( well_known::PAIRS.to_string(), TypeConstructor::primitive(Type::map( Type::generic_var(id_gen.next()), Type::generic_var(id_gen.next()), )), ); // Option(value) let option_value = Type::generic_var(id_gen.next()); prelude.types.insert( well_known::OPTION.to_string(), TypeConstructor::primitive(Type::option(option_value.clone())), ); let some_type = Type::function( vec![option_value.clone()], Type::option(option_value.clone()), ); let none_type = Type::option(option_value); prelude.types_constructors.insert( well_known::OPTION.to_string(), ValueConstructor::known_adt( &mut prelude.values, &[ (well_known::OPTION_CONSTRUCTORS[0], some_type), (well_known::OPTION_CONSTRUCTORS[1], none_type), ], ), ); // Cardano ScriptContext prelude.types.insert( well_known::SCRIPT_CONTEXT.to_string(), TypeConstructor::primitive(Type::script_context()), ); prelude.types_constructors.insert( well_known::SCRIPT_CONTEXT.to_string(), vec![ well_known::SCRIPT_CONTEXT_TRANSACTION.to_string(), well_known::SCRIPT_CONTEXT_REDEEMER.to_string(), well_known::SCRIPT_CONTEXT_PURPOSE.to_string(), ], ); // Cardano ScriptPurpose prelude.types.insert( well_known::SCRIPT_PURPOSE.to_string(), TypeConstructor::primitive(Type::script_purpose()), ); prelude.types_constructors.insert( well_known::SCRIPT_PURPOSE.to_string(), ValueConstructor::known_adt( &mut prelude.values, &[ ( well_known::SCRIPT_PURPOSE_MINT, Type::function(vec![Type::data()], Type::script_purpose()), ), ( well_known::SCRIPT_PURPOSE_SPEND, Type::function( vec![Type::data(), Type::option(Type::data())], Type::script_purpose(), ), ), ( well_known::SCRIPT_PURPOSE_WITHDRAW, Type::function(vec![Type::data()], Type::script_purpose()), ), ( well_known::SCRIPT_PURPOSE_PUBLISH, Type::function(vec![Type::int(), Type::data()], Type::script_purpose()), ), ( well_known::SCRIPT_PURPOSE_VOTE, Type::function(vec![Type::data()], Type::script_purpose()), ), ( well_known::SCRIPT_PURPOSE_PROPOSE, Type::function(vec![Type::int(), Type::data()], Type::script_purpose()), ), ], ), ); // not prelude.values.insert( "not".to_string(), ValueConstructor::public( Type::function(vec![Type::bool()], Type::bool()), ValueConstructorVariant::ModuleFn { name: "not".to_string(), field_map: None, module: "".to_string(), arity: 1, location: Span::empty(), builtin: None, }, ), ); // identity let identity_var = Type::generic_var(id_gen.next()); prelude.values.insert( "identity".to_string(), ValueConstructor::public( Type::function(vec![identity_var.clone()], identity_var), ValueConstructorVariant::ModuleFn { name: "identity".to_string(), field_map: None, module: "".to_string(), arity: 1, location: Span::empty(), builtin: None, }, ), ); // enumerate let enumerate_a = Type::generic_var(id_gen.next()); let enumerate_b = Type::generic_var(id_gen.next()); prelude.values.insert( "enumerate".to_string(), ValueConstructor::public( Type::function( vec![ Type::list(enumerate_a.clone()), enumerate_b.clone(), Type::function( vec![enumerate_a.clone(), enumerate_b.clone()], enumerate_b.clone(), ), Type::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( Type::function( vec![Type::byte_array(), Type::int(), Type::byte_array()], Type::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( Type::function(vec![Type::int(), Type::byte_array()], Type::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( Type::function(vec![Type::int(), Type::byte_array()], Type::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( Type::function(vec![Type::data(), Type::byte_array()], Type::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 = Type::generic_var(id_gen.next()); let always_b_var = Type::generic_var(id_gen.next()); prelude.values.insert( "always".to_string(), ValueConstructor::public( Type::function(vec![always_a_var.clone(), always_b_var], always_a_var), ValueConstructorVariant::ModuleFn { name: "always".to_string(), field_map: None, module: "".to_string(), arity: 2, location: Span::empty(), builtin: None, }, ), ); // flip let flip_a_var = Type::generic_var(id_gen.next()); let flip_b_var = Type::generic_var(id_gen.next()); let flip_c_var = Type::generic_var(id_gen.next()); let input_type = Type::function( vec![flip_a_var.clone(), flip_b_var.clone()], flip_c_var.clone(), ); let return_type = Type::function(vec![flip_b_var, flip_a_var], flip_c_var); prelude.values.insert( "flip".to_string(), ValueConstructor::public( Type::function(vec![input_type], return_type), ValueConstructorVariant::ModuleFn { name: "flip".to_string(), field_map: None, module: "".to_string(), arity: 1, location: Span::empty(), builtin: None, }, ), ); // PRNG // // pub type PRNG { // Seeded { seed: ByteArray, choices: ByteArray } // Replayed { cursor: Int, choices: ByteArray } // } prelude.types.insert( well_known::PRNG.to_string(), TypeConstructor::primitive(Type::prng()), ); prelude.types_constructors.insert( well_known::PRNG.to_string(), vec!["Seeded".to_string(), "Replayed".to_string()], ); let mut seeded_fields = HashMap::new(); seeded_fields.insert("seed".to_string(), (0, Span::empty())); seeded_fields.insert("choices".to_string(), (1, Span::empty())); prelude.values.insert( "Seeded".to_string(), ValueConstructor::public( Type::function(vec![Type::byte_array(), Type::byte_array()], Type::prng()), ValueConstructorVariant::Record { module: "".into(), name: "Seeded".to_string(), field_map: Some(FieldMap { arity: 2, fields: seeded_fields, is_function: false, }), arity: 2, location: Span::empty(), constructors_count: 2, }, ), ); let mut replayed_fields = HashMap::new(); replayed_fields.insert("cursor".to_string(), (0, Span::empty())); replayed_fields.insert("choices".to_string(), (1, Span::empty())); prelude.values.insert( "Replayed".to_string(), ValueConstructor::public( Type::function(vec![Type::int(), Type::byte_array()], Type::prng()), ValueConstructorVariant::Record { module: "".into(), name: "Replayed".to_string(), field_map: Some(FieldMap { arity: 2, fields: replayed_fields, is_function: false, }), arity: 2, location: Span::empty(), constructors_count: 2, }, ), ); // Fuzzer // // pub type Fuzzer = // fn(PRNG) -> Option<(PRNG, a)> let fuzzer_value = Type::generic_var(id_gen.next()); prelude.types.insert( well_known::FUZZER.to_string(), TypeConstructor { location: Span::empty(), parameters: vec![fuzzer_value.clone()], tipo: Type::fuzzer(fuzzer_value), module: "".to_string(), public: true, }, ); prelude } pub fn plutus(id_gen: &IdGenerator) -> TypeInfo { let mut plutus = TypeInfo { name: BUILTIN.to_string(), package: "".to_string(), kind: ModuleKind::Lib, types: HashMap::new(), types_constructors: HashMap::new(), values: HashMap::new(), accessors: HashMap::new(), annotations: HashMap::new(), }; for builtin in DefaultFunction::iter() { let value = from_default_function(builtin, id_gen); plutus.values.insert(builtin.aiken_name(), value); } plutus } pub fn from_default_function(builtin: DefaultFunction, id_gen: &IdGenerator) -> ValueConstructor { let (tipo, arity) = match builtin { DefaultFunction::AddInteger | DefaultFunction::SubtractInteger | DefaultFunction::MultiplyInteger | DefaultFunction::DivideInteger | DefaultFunction::QuotientInteger | DefaultFunction::RemainderInteger | DefaultFunction::ModInteger => { let tipo = Type::function(vec![Type::int(), Type::int()], Type::int()); (tipo, 2) } DefaultFunction::EqualsInteger | DefaultFunction::LessThanInteger | DefaultFunction::LessThanEqualsInteger => { let tipo = Type::function(vec![Type::int(), Type::int()], Type::bool()); (tipo, 2) } DefaultFunction::AppendByteString => { let tipo = Type::function( vec![Type::byte_array(), Type::byte_array()], Type::byte_array(), ); (tipo, 2) } DefaultFunction::ConsByteString => { let tipo = Type::function(vec![Type::int(), Type::byte_array()], Type::byte_array()); (tipo, 2) } DefaultFunction::SliceByteString => { let tipo = Type::function( vec![Type::int(), Type::int(), Type::byte_array()], Type::byte_array(), ); (tipo, 3) } DefaultFunction::LengthOfByteString => { let tipo = Type::function(vec![Type::byte_array()], Type::int()); (tipo, 1) } DefaultFunction::IndexByteString => { let tipo = Type::function(vec![Type::byte_array(), Type::int()], Type::int()); (tipo, 2) } DefaultFunction::EqualsByteString | DefaultFunction::LessThanByteString | DefaultFunction::LessThanEqualsByteString => { let tipo = Type::function(vec![Type::byte_array(), Type::byte_array()], Type::bool()); (tipo, 2) } DefaultFunction::Sha2_256 | DefaultFunction::Sha3_256 | DefaultFunction::Blake2b_224 | DefaultFunction::Blake2b_256 | DefaultFunction::Keccak_256 => { let tipo = Type::function(vec![Type::byte_array()], Type::byte_array()); (tipo, 1) } DefaultFunction::VerifyEd25519Signature => { let tipo = Type::function( vec![Type::byte_array(), Type::byte_array(), Type::byte_array()], Type::bool(), ); (tipo, 3) } DefaultFunction::VerifyEcdsaSecp256k1Signature => { let tipo = Type::function( vec![Type::byte_array(), Type::byte_array(), Type::byte_array()], Type::bool(), ); (tipo, 3) } DefaultFunction::VerifySchnorrSecp256k1Signature => { let tipo = Type::function( vec![Type::byte_array(), Type::byte_array(), Type::byte_array()], Type::bool(), ); (tipo, 3) } DefaultFunction::AppendString => { let tipo = Type::function(vec![Type::string(), Type::string()], Type::string()); (tipo, 2) } DefaultFunction::EqualsString => { let tipo = Type::function(vec![Type::string(), Type::string()], Type::bool()); (tipo, 2) } DefaultFunction::EncodeUtf8 => { let tipo = Type::function(vec![Type::string()], Type::byte_array()); (tipo, 1) } DefaultFunction::DecodeUtf8 => { let tipo = Type::function(vec![Type::byte_array()], Type::string()); (tipo, 1) } DefaultFunction::IfThenElse => { let ret = Type::generic_var(id_gen.next()); let tipo = Type::function(vec![Type::bool(), ret.clone(), ret.clone()], ret); (tipo, 3) } DefaultFunction::HeadList => { let ret = Type::generic_var(id_gen.next()); let tipo = Type::function(vec![Type::list(ret.clone())], ret); (tipo, 1) } DefaultFunction::TailList => { let ret = Type::list(Type::generic_var(id_gen.next())); let tipo = Type::function(vec![ret.clone()], ret); (tipo, 1) } DefaultFunction::NullList => { let ret = Type::list(Type::generic_var(id_gen.next())); let tipo = Type::function(vec![ret], Type::bool()); (tipo, 1) } DefaultFunction::ConstrData => { let tipo = Type::function(vec![Type::int(), Type::list(Type::data())], Type::data()); (tipo, 2) } DefaultFunction::MapData => { let tipo = Type::function( vec![Type::list(Type::pair(Type::data(), Type::data()))], Type::data(), ); (tipo, 1) } DefaultFunction::ListData => { let tipo = Type::function(vec![Type::list(Type::data())], Type::data()); (tipo, 1) } DefaultFunction::IData => { let tipo = Type::function(vec![Type::int()], Type::data()); (tipo, 1) } DefaultFunction::BData => { let tipo = Type::function(vec![Type::byte_array()], Type::data()); (tipo, 1) } DefaultFunction::UnConstrData => { let tipo = Type::function( vec![Type::data()], Type::pair(Type::int(), Type::list(Type::data())), ); (tipo, 1) } DefaultFunction::UnMapData => { let tipo = Type::function( vec![Type::data()], Type::list(Type::pair(Type::data(), Type::data())), ); (tipo, 1) } DefaultFunction::UnListData => { let tipo = Type::function(vec![Type::data()], Type::list(Type::data())); (tipo, 1) } DefaultFunction::UnIData => { let tipo = Type::function(vec![Type::data()], Type::int()); (tipo, 1) } DefaultFunction::UnBData => { let tipo = Type::function(vec![Type::data()], Type::byte_array()); (tipo, 1) } DefaultFunction::EqualsData => { let tipo = Type::function(vec![Type::data(), Type::data()], Type::bool()); (tipo, 2) } DefaultFunction::SerialiseData => { let tipo = Type::function(vec![Type::data()], Type::byte_array()); (tipo, 1) } DefaultFunction::ChooseData => { let a = Type::generic_var(id_gen.next()); let tipo = Type::function( vec![ Type::data(), a.clone(), a.clone(), a.clone(), a.clone(), a.clone(), ], a, ); (tipo, 6) } DefaultFunction::MkPairData => { let tipo = Type::function( vec![Type::data(), Type::data()], Type::pair(Type::data(), Type::data()), ); (tipo, 2) } DefaultFunction::MkNilData => { let tipo = Type::function(vec![], Type::list(Type::data())); (tipo, 0) } DefaultFunction::MkNilPairData => { let tipo = Type::function(vec![], Type::list(Type::pair(Type::data(), Type::data()))); (tipo, 0) } DefaultFunction::ChooseUnit => { let a = Type::generic_var(id_gen.next()); let tipo = Type::function(vec![Type::data(), a.clone()], a); (tipo, 2) } DefaultFunction::Trace => { let a = Type::generic_var(id_gen.next()); let tipo = Type::function(vec![Type::string(), a.clone()], a); (tipo, 2) } DefaultFunction::FstPair => { let a = Type::generic_var(id_gen.next()); let b = Type::generic_var(id_gen.next()); let tipo = Type::function(vec![Type::pair(a.clone(), b)], a); (tipo, 1) } DefaultFunction::SndPair => { let a = Type::generic_var(id_gen.next()); let b = Type::generic_var(id_gen.next()); let tipo = Type::function(vec![Type::pair(a, b.clone())], b); (tipo, 1) } DefaultFunction::ChooseList => { let a = Type::generic_var(id_gen.next()); let b = Type::generic_var(id_gen.next()); let tipo = Type::function(vec![Type::list(a), b.clone(), b.clone()], b); (tipo, 3) } DefaultFunction::MkCons => { let a = Type::generic_var(id_gen.next()); let tipo = Type::function(vec![a.clone(), Type::list(a.clone())], Type::list(a)); (tipo, 2) } DefaultFunction::Bls12_381_G1_Add => { let tipo = Type::function( vec![Type::g1_element(), Type::g1_element()], Type::g1_element(), ); (tipo, 2) } DefaultFunction::Bls12_381_G1_Equal => { let tipo = Type::function(vec![Type::g1_element(), Type::g1_element()], Type::bool()); (tipo, 2) } DefaultFunction::Bls12_381_G1_Neg => { let tipo = Type::function(vec![Type::g1_element()], Type::g1_element()); (tipo, 1) } DefaultFunction::Bls12_381_G1_ScalarMul => { let tipo = Type::function(vec![Type::int(), Type::g1_element()], Type::g1_element()); (tipo, 2) } DefaultFunction::Bls12_381_G1_Compress => { let tipo = Type::function(vec![Type::g1_element()], Type::byte_array()); (tipo, 1) } DefaultFunction::Bls12_381_G1_Uncompress => { let tipo = Type::function(vec![Type::byte_array()], Type::g1_element()); (tipo, 1) } DefaultFunction::Bls12_381_G1_HashToGroup => { let tipo = Type::function( vec![Type::byte_array(), Type::byte_array()], Type::g1_element(), ); (tipo, 2) } DefaultFunction::Bls12_381_G2_Add => { let tipo = Type::function( vec![Type::g2_element(), Type::g2_element()], Type::g2_element(), ); (tipo, 2) } DefaultFunction::Bls12_381_G2_Equal => { let tipo = Type::function(vec![Type::g2_element(), Type::g2_element()], Type::bool()); (tipo, 2) } DefaultFunction::Bls12_381_G2_Neg => { let tipo = Type::function(vec![Type::g2_element()], Type::g2_element()); (tipo, 1) } DefaultFunction::Bls12_381_G2_ScalarMul => { let tipo = Type::function(vec![Type::int(), Type::g2_element()], Type::g2_element()); (tipo, 2) } DefaultFunction::Bls12_381_G2_Compress => { let tipo = Type::function(vec![Type::g2_element()], Type::byte_array()); (tipo, 1) } DefaultFunction::Bls12_381_G2_Uncompress => { let tipo = Type::function(vec![Type::byte_array()], Type::g2_element()); (tipo, 1) } DefaultFunction::Bls12_381_G2_HashToGroup => { let tipo = Type::function( vec![Type::byte_array(), Type::byte_array()], Type::g2_element(), ); (tipo, 2) } DefaultFunction::Bls12_381_MillerLoop => { let tipo = Type::function( vec![Type::g1_element(), Type::g2_element()], Type::miller_loop_result(), ); (tipo, 2) } DefaultFunction::Bls12_381_MulMlResult => { let tipo = Type::function( vec![Type::miller_loop_result(), Type::miller_loop_result()], Type::miller_loop_result(), ); (tipo, 2) } DefaultFunction::Bls12_381_FinalVerify => { let tipo = Type::function( vec![Type::miller_loop_result(), Type::miller_loop_result()], Type::bool(), ); (tipo, 2) } DefaultFunction::IntegerToByteString => { let tipo = Type::function( vec![Type::bool(), Type::int(), Type::int()], Type::byte_array(), ); (tipo, 3) } DefaultFunction::ByteStringToInteger => { let tipo = Type::function(vec![Type::bool(), Type::byte_array()], Type::int()); (tipo, 2) } }; ValueConstructor::public( tipo, ValueConstructorVariant::ModuleFn { name: builtin.aiken_name(), field_map: None, module: "".to_string(), arity, location: Span::empty(), builtin: Some(builtin), }, ) } pub fn prelude_functions( id_gen: &IdGenerator, module_types: &HashMap, ) -> IndexMap { let mut functions = IndexMap::new(); // /// Negate the argument. Useful for map/fold and pipelines. // pub fn not(self: Bool) -> Bool { // !self // } functions.insert( FunctionAccessKey { module_name: "".to_string(), function_name: "not".to_string(), }, Function { arguments: vec![TypedArg { arg_name: ArgName::Named { name: "self".to_string(), label: "self".to_string(), location: Span::empty(), }, is_validator_param: false, doc: None, location: Span::empty(), annotation: None, tipo: Type::bool(), }], on_test_failure: OnTestFailure::FailImmediately, doc: Some( indoc::indoc! { r#" /// Like `!`, but as a function. Handy for chaining using the pipe operator `|>` or to pass as a function. "# }.to_string() ), location: Span::empty(), name: "not".to_string(), public: true, return_annotation: None, return_type: Type::bool(), end_position: 0, body: TypedExpr::UnOp { location: Span::empty(), tipo: Type::bool(), op: UnOp::Not, value: Box::new(TypedExpr::Var { location: Span::empty(), constructor: ValueConstructor { public: true, tipo: Type::bool(), variant: ValueConstructorVariant::LocalVariable { location: Span::empty(), }, }, name: "self".to_string(), }), }, }, ); // /// A function that returns its argument. Handy as a default behavior sometimes. // pub fn identity(a: a) -> a { // a // } let a_var = Type::generic_var(id_gen.next()); functions.insert( FunctionAccessKey { module_name: "".to_string(), function_name: "identity".to_string(), }, Function { arguments: vec![TypedArg { arg_name: ArgName::Named { name: "a".to_string(), label: "a".to_string(), location: Span::empty(), }, is_validator_param: false, location: Span::empty(), annotation: None, doc: None, tipo: a_var.clone(), }], on_test_failure: OnTestFailure::FailImmediately, body: TypedExpr::Var { location: Span::empty(), constructor: ValueConstructor { public: true, tipo: a_var.clone(), variant: ValueConstructorVariant::LocalVariable { location: Span::empty(), }, }, name: "a".to_string(), }, doc: Some( indoc::indoc! { r#" A function that returns its argument. Handy as a default behavior sometimes. "# } .to_string(), ), location: Span::empty(), name: "identity".to_string(), public: true, return_annotation: None, return_type: a_var, end_position: 0, }, ); // /// A function that always return its first argument. Handy in folds and maps. // pub fn always(a: a, b _b: b) -> a { // a // } let a_var = Type::generic_var(id_gen.next()); let b_var = Type::generic_var(id_gen.next()); functions.insert( FunctionAccessKey { module_name: "".to_string(), function_name: "always".to_string(), }, Function { on_test_failure: OnTestFailure::FailImmediately, arguments: vec![ TypedArg { arg_name: ArgName::Named { name: "a".to_string(), label: "a".to_string(), location: Span::empty(), }, is_validator_param: false, location: Span::empty(), annotation: None, doc: None, tipo: a_var.clone(), }, TypedArg { arg_name: ArgName::Discarded { name: "_b".to_string(), label: "_b".to_string(), location: Span::empty(), }, is_validator_param: false, location: Span::empty(), annotation: None, doc: None, tipo: b_var, }, ], body: TypedExpr::Var { location: Span::empty(), constructor: ValueConstructor { public: true, tipo: a_var.clone(), variant: ValueConstructorVariant::LocalVariable { location: Span::empty(), }, }, name: "a".to_string(), }, doc: Some( indoc::indoc! { r#" A function that always return its first argument. Handy in folds and maps. ```aiken let always_14 = always(14, _) always_14(42) == 14 always_14(1337) == 14 always_14(0) == 14 ``` "# } .to_string(), ), location: Span::empty(), name: "always".to_string(), public: true, return_annotation: None, return_type: a_var, end_position: 0, }, ); // /// A function that flips the arguments of a function. // pub fn flip(f: fn(a, b) -> c) -> fn(b, a) -> c { // fn(b, a) { f(a, b) } // } let a_var = Type::generic_var(id_gen.next()); let b_var = Type::generic_var(id_gen.next()); let c_var = Type::generic_var(id_gen.next()); let input_type = Type::function(vec![a_var.clone(), b_var.clone()], c_var.clone()); let return_type = Type::function(vec![b_var.clone(), a_var.clone()], c_var.clone()); functions.insert( FunctionAccessKey { module_name: "".to_string(), function_name: "flip".to_string(), }, Function { on_test_failure: OnTestFailure::FailImmediately, arguments: vec![TypedArg { arg_name: ArgName::Named { name: "f".to_string(), label: "f".to_string(), location: Span::empty(), }, is_validator_param: false, location: Span::empty(), annotation: None, doc: None, tipo: input_type.clone(), }], body: TypedExpr::Fn { location: Span::empty(), tipo: return_type.clone(), is_capture: false, args: vec![ TypedArg { arg_name: ArgName::Named { name: "b".to_string(), label: "b".to_string(), location: Span::empty(), }, is_validator_param: false, location: Span::empty(), annotation: None, doc: None, tipo: b_var.clone(), }, TypedArg { arg_name: ArgName::Named { name: "a".to_string(), label: "a".to_string(), location: Span::empty(), }, is_validator_param: false, location: Span::empty(), annotation: None, doc: None, tipo: a_var.clone(), }, ], body: Box::new(TypedExpr::Call { location: Span::empty(), tipo: c_var, fun: Box::new(TypedExpr::Var { location: Span::empty(), constructor: ValueConstructor { public: true, tipo: input_type, variant: ValueConstructorVariant::LocalVariable { location: Span::empty(), }, }, name: "f".to_string(), }), args: vec![ CallArg { label: None, location: Span::empty(), value: TypedExpr::Var { location: Span::empty(), constructor: ValueConstructor { public: true, tipo: a_var, variant: ValueConstructorVariant::LocalVariable { location: Span::empty(), }, }, name: "a".to_string(), }, }, CallArg { label: None, location: Span::empty(), value: TypedExpr::Var { location: Span::empty(), constructor: ValueConstructor { public: true, tipo: b_var, variant: ValueConstructorVariant::LocalVariable { location: Span::empty(), }, }, name: "b".to_string(), }, }, ], }), return_annotation: None, }, doc: Some( indoc::indoc! { r#" A function that flips the arguments of a function. ```aiken pub fn titleize(left: String, right: String) {} titleize("Hello", "World") // "Hello, World!" flip(titleize)("Hello", "World") // "World, Hello!" ``` "# } .to_string(), ), location: Span::empty(), name: "flip".to_string(), public: true, return_annotation: None, return_type, end_position: 0, }, ); functions.insert( FunctionAccessKey { module_name: "".to_string(), function_name: "enumerate".to_string(), }, aiken_fn!( &module_types, &id_gen, r#" fn enumerate( self: List, 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, st: ByteArray) { let value = diagnostic(e.2nd, builtin.append_bytearray(#"2c20", st)) diagnostic(e.1st, builtin.append_bytearray(#"3a20", value)) }, fn(e: Pair, 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 } pub fn prelude_data_types(id_gen: &IdGenerator) -> IndexMap { let mut data_types = IndexMap::new(); // Data let data_data_type = TypedDataType::data(); data_types.insert( DataTypeKey { module_name: "".to_string(), defined_type: well_known::DATA.to_string(), }, data_data_type, ); // Ordering let ordering_data_type = TypedDataType::ordering(); data_types.insert( DataTypeKey { module_name: "".to_string(), defined_type: well_known::ORDERING.to_string(), }, ordering_data_type, ); // Bool let bool_data_type = TypedDataType::bool(); data_types.insert( DataTypeKey { module_name: "".to_string(), defined_type: well_known::BOOL.to_string(), }, bool_data_type, ); // Option let option_data_type = TypedDataType::option(Type::generic_var(id_gen.next())); data_types.insert( DataTypeKey { module_name: "".to_string(), defined_type: well_known::OPTION.to_string(), }, option_data_type, ); // PRNG let prng_data_type = TypedDataType::prng(); data_types.insert( DataTypeKey { module_name: "".to_string(), defined_type: well_known::PRNG.to_string(), }, prng_data_type, ); // __ScriptPurpose let script_purpose_data_type = TypedDataType::script_purpose(); data_types.insert( DataTypeKey { module_name: "".to_string(), defined_type: well_known::SCRIPT_PURPOSE.to_string(), }, script_purpose_data_type, ); // __ScriptContext let script_context_data_type = TypedDataType::script_context(); data_types.insert( DataTypeKey { module_name: "".to_string(), defined_type: well_known::SCRIPT_CONTEXT.to_string(), }, script_context_data_type, ); data_types } // ---------------------------------------------------------------------------- // TypedDataTypes // // TODO: Rewrite in terms of ValueConstructor to avoid duplication and ensure // consistency with prelude definitions. impl TypedDataType { pub fn data() -> Self { DataType::known_enum(well_known::DATA, &[]) } pub fn bool() -> Self { DataType::known_enum(well_known::BOOL, well_known::BOOL_CONSTRUCTORS) } pub fn script_purpose() -> Self { DataType::known_enum( well_known::SCRIPT_PURPOSE, well_known::SCRIPT_PURPOSE_CONSTRUCTORS, ) } pub fn script_context() -> Self { DataType::known_enum( well_known::SCRIPT_CONTEXT, well_known::SCRIPT_CONTEXT_CONSTRUCTORS, ) } pub fn prng() -> Self { DataType::known_enum(well_known::PRNG, well_known::PRNG_CONSTRUCTORS) } pub fn ordering() -> Self { DataType::known_enum(well_known::ORDERING, well_known::ORDERING_CONSTRUCTORS) } pub fn option(tipo: Rc) -> Self { DataType { constructors: vec![ RecordConstructor { location: Span::empty(), name: well_known::OPTION_CONSTRUCTORS[0].to_string(), arguments: vec![RecordConstructorArg { label: None, annotation: Annotation::Var { location: Span::empty(), name: "a".to_string(), }, location: Span::empty(), tipo: tipo.clone(), doc: None, }], doc: None, sugar: false, }, RecordConstructor { location: Span::empty(), name: well_known::OPTION_CONSTRUCTORS[1].to_string(), arguments: vec![], doc: None, sugar: false, }, ], doc: None, location: Span::empty(), name: well_known::OPTION.to_string(), opaque: false, parameters: vec!["a".to_string()], public: true, typed_parameters: vec![tipo], } } }