diff --git a/CHANGELOG.md b/CHANGELOG.md index 86b59907..ccc2c32e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ **aiken**: Fancy errors using [miette](https://github.com/zkat/miette) **aiken**: Typechecking +**aiken**: Inject `aiken/builtin` module with some functions from `DefaultFunction` in UPLC directly exposed **aiken-lang**: add `infer` method to `UntypedModule` which returns a `TypedModule` ### Changed diff --git a/Cargo.lock b/Cargo.lock index 6907b6d0..38d31c98 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -80,6 +80,7 @@ dependencies = [ "itertools", "miette", "pretty_assertions", + "strum", "thiserror", "uplc", "vec1", @@ -928,6 +929,12 @@ version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" +[[package]] +name = "rustversion" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" + [[package]] name = "rusty-fork" version = "0.3.0" @@ -1010,6 +1017,25 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strum" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" + +[[package]] +name = "strum_macros" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + [[package]] name = "supports-color" version = "1.3.0" @@ -1192,6 +1218,8 @@ dependencies = [ "proptest", "serde", "serde_json", + "strum", + "strum_macros", "thiserror", ] diff --git a/crates/cli/src/project.rs b/crates/cli/src/project.rs index 7e53bea3..bc5ab999 100644 --- a/crates/cli/src/project.rs +++ b/crates/cli/src/project.rs @@ -37,7 +37,7 @@ impl Project { let mut module_types = HashMap::new(); module_types.insert("aiken".to_string(), builtins::prelude(&id_gen)); - module_types.insert("aiken/builtin".to_string(), builtins::plutus()); + module_types.insert("aiken/builtin".to_string(), builtins::plutus(&id_gen)); Project { config, diff --git a/crates/lang/Cargo.toml b/crates/lang/Cargo.toml index 29fd52a7..ea87771c 100644 --- a/crates/lang/Cargo.toml +++ b/crates/lang/Cargo.toml @@ -15,6 +15,7 @@ chumsky = "0.8.0" internment = "0.7.0" itertools = "0.10.5" miette = "5.2.0" +strum = "0.24.1" thiserror = "1.0.37" uplc = { path = '../uplc', version = "0.0.21" } vec1 = "1.8.0" diff --git a/crates/lang/src/builtins.rs b/crates/lang/src/builtins.rs index e4cc5de5..f033b917 100644 --- a/crates/lang/src/builtins.rs +++ b/crates/lang/src/builtins.rs @@ -1,5 +1,7 @@ use std::{cell::RefCell, collections::HashMap, sync::Arc}; +use strum::IntoEnumIterator; + use uplc::builtins::DefaultFunction; use crate::{ @@ -212,7 +214,7 @@ pub fn prelude(id_gen: &IdGenerator) -> TypeInfo { prelude } -pub fn plutus() -> TypeInfo { +pub fn plutus(id_gen: &IdGenerator) -> TypeInfo { let mut plutus = TypeInfo { name: "aiken/builtin".to_string(), package: "".to_string(), @@ -223,101 +225,174 @@ pub fn plutus() -> TypeInfo { accessors: HashMap::new(), }; - plutus.values.insert( - DefaultFunction::AddInteger.to_string(), - DefaultFunction::AddInteger.into(), - ); - - plutus.values.insert( - DefaultFunction::SubtractInteger.to_string(), - DefaultFunction::SubtractInteger.into(), - ); - - plutus.values.insert( - DefaultFunction::MultiplyInteger.to_string(), - DefaultFunction::MultiplyInteger.into(), - ); - - plutus.values.insert( - DefaultFunction::DivideInteger.to_string(), - DefaultFunction::DivideInteger.into(), - ); + for builtin in DefaultFunction::iter() { + if let Some(value) = from_default_function(builtin, id_gen) { + plutus.values.insert( + builtin.to_string().replace("ByteString", "ByteArray"), + value, + ); + } + } plutus } -impl From for ValueConstructor { - fn from(builtin: DefaultFunction) -> Self { - let (tipo, arity) = match builtin { - DefaultFunction::AddInteger - | DefaultFunction::SubtractInteger - | DefaultFunction::MultiplyInteger - | DefaultFunction::DivideInteger - | DefaultFunction::QuotientInteger - | DefaultFunction::RemainderInteger - | DefaultFunction::ModInteger => { - let tipo = function(vec![int(), int()], int()); +pub fn from_default_function( + builtin: DefaultFunction, + id_gen: &IdGenerator, +) -> Option { + let info = match builtin { + DefaultFunction::AddInteger + | DefaultFunction::SubtractInteger + | DefaultFunction::MultiplyInteger + | DefaultFunction::DivideInteger + | DefaultFunction::QuotientInteger + | DefaultFunction::RemainderInteger + | DefaultFunction::ModInteger => { + let tipo = function(vec![int(), int()], int()); - (tipo, 2) - } + Some((tipo, 2)) + } - DefaultFunction::EqualsInteger - | DefaultFunction::LessThanInteger - | DefaultFunction::LessThanEqualsInteger => { - let tipo = function(vec![int(), int()], bool()); + DefaultFunction::EqualsInteger + | DefaultFunction::LessThanInteger + | DefaultFunction::LessThanEqualsInteger => { + let tipo = function(vec![int(), int()], bool()); - (tipo, 2) - } - DefaultFunction::AppendByteString => todo!(), - DefaultFunction::ConsByteString => todo!(), - DefaultFunction::SliceByteString => todo!(), - DefaultFunction::LengthOfByteString => todo!(), - DefaultFunction::IndexByteString => todo!(), - DefaultFunction::EqualsByteString => todo!(), - DefaultFunction::LessThanByteString => todo!(), - DefaultFunction::LessThanEqualsByteString => todo!(), - DefaultFunction::Sha2_256 => todo!(), - DefaultFunction::Sha3_256 => todo!(), - DefaultFunction::Blake2b_256 => todo!(), - DefaultFunction::VerifyEd25519Signature => todo!(), - DefaultFunction::VerifyEcdsaSecp256k1Signature => todo!(), - DefaultFunction::VerifySchnorrSecp256k1Signature => todo!(), - DefaultFunction::AppendString => todo!(), - DefaultFunction::EqualsString => todo!(), - DefaultFunction::EncodeUtf8 => todo!(), - DefaultFunction::DecodeUtf8 => todo!(), - DefaultFunction::IfThenElse => todo!(), - DefaultFunction::ChooseUnit => todo!(), - DefaultFunction::Trace => todo!(), - DefaultFunction::FstPair => todo!(), - DefaultFunction::SndPair => todo!(), - DefaultFunction::ChooseList => todo!(), - DefaultFunction::MkCons => todo!(), - DefaultFunction::HeadList => todo!(), - DefaultFunction::TailList => todo!(), - DefaultFunction::NullList => todo!(), - DefaultFunction::ChooseData => todo!(), - DefaultFunction::ConstrData => todo!(), - DefaultFunction::MapData => todo!(), - DefaultFunction::ListData => todo!(), - DefaultFunction::IData => todo!(), - DefaultFunction::BData => todo!(), - DefaultFunction::UnConstrData => todo!(), - DefaultFunction::UnMapData => todo!(), - DefaultFunction::UnListData => todo!(), - DefaultFunction::UnIData => todo!(), - DefaultFunction::UnBData => todo!(), - DefaultFunction::EqualsData => todo!(), - DefaultFunction::SerialiseData => todo!(), - DefaultFunction::MkPairData => todo!(), - DefaultFunction::MkNilData => todo!(), - DefaultFunction::MkNilPairData => todo!(), - }; + Some((tipo, 2)) + } + DefaultFunction::AppendByteString => { + let tipo = function(vec![byte_array(), byte_array()], byte_array()); + Some((tipo, 2)) + } + DefaultFunction::ConsByteString => { + let tipo = function(vec![int(), byte_array()], byte_array()); + + Some((tipo, 2)) + } + DefaultFunction::SliceByteString => { + let tipo = function(vec![int(), int(), byte_array()], byte_array()); + + Some((tipo, 3)) + } + DefaultFunction::LengthOfByteString => { + let tipo = function(vec![byte_array()], int()); + + Some((tipo, 1)) + } + DefaultFunction::IndexByteString => { + let tipo = function(vec![byte_array(), int()], int()); + + Some((tipo, 2)) + } + DefaultFunction::EqualsByteString + | DefaultFunction::LessThanByteString + | DefaultFunction::LessThanEqualsByteString => { + let tipo = function(vec![byte_array(), byte_array()], bool()); + + Some((tipo, 2)) + } + DefaultFunction::Sha2_256 | DefaultFunction::Sha3_256 | DefaultFunction::Blake2b_256 => { + let tipo = function(vec![byte_array()], byte_array()); + + Some((tipo, 1)) + } + + DefaultFunction::VerifyEd25519Signature => { + let tipo = function(vec![byte_array(), byte_array(), byte_array()], bool()); + + Some((tipo, 3)) + } + DefaultFunction::VerifyEcdsaSecp256k1Signature => None, + DefaultFunction::VerifySchnorrSecp256k1Signature => None, + DefaultFunction::AppendString => { + let tipo = function(vec![string(), string()], string()); + + Some((tipo, 2)) + } + DefaultFunction::EqualsString => { + let tipo = function(vec![string(), string()], bool()); + + Some((tipo, 2)) + } + DefaultFunction::EncodeUtf8 => { + let tipo = function(vec![string()], byte_array()); + + Some((tipo, 1)) + } + DefaultFunction::DecodeUtf8 => { + let tipo = function(vec![byte_array()], string()); + + Some((tipo, 1)) + } + DefaultFunction::IfThenElse => None, + DefaultFunction::ChooseUnit => None, + DefaultFunction::Trace => { + let ret = generic_var(id_gen.next()); + + let tipo = function(vec![string(), ret.clone()], ret); + + Some((tipo, 2)) + } + DefaultFunction::FstPair => None, + DefaultFunction::SndPair => None, + DefaultFunction::ChooseList => None, + DefaultFunction::MkCons => None, + DefaultFunction::HeadList => { + let ret = generic_var(id_gen.next()); + + let tipo = function(vec![list(ret.clone())], ret); + + Some((tipo, 1)) + } + DefaultFunction::TailList => { + let ret = list(generic_var(id_gen.next())); + + let tipo = function(vec![ret.clone()], ret); + + Some((tipo, 1)) + } + DefaultFunction::NullList => { + let ret = list(generic_var(id_gen.next())); + + let tipo = function(vec![ret], bool()); + + Some((tipo, 1)) + } + DefaultFunction::ChooseData => None, + DefaultFunction::ConstrData => None, + DefaultFunction::MapData => None, + DefaultFunction::ListData => None, + DefaultFunction::IData => None, + DefaultFunction::BData => None, + DefaultFunction::UnConstrData => None, + DefaultFunction::UnMapData => None, + DefaultFunction::UnListData => None, + DefaultFunction::UnIData => None, + DefaultFunction::UnBData => None, + DefaultFunction::EqualsData => { + let arg = generic_var(id_gen.next()); + + let tipo = function(vec![arg.clone(), arg], bool()); + + Some((tipo, 1)) + } + DefaultFunction::SerialiseData => { + let tipo = function(vec![generic_var(id_gen.next())], byte_array()); + + Some((tipo, 1)) + } + DefaultFunction::MkPairData => None, + DefaultFunction::MkNilData => None, + DefaultFunction::MkNilPairData => None, + }; + + info.map(|(tipo, arity)| { ValueConstructor::public( tipo, ValueConstructorVariant::ModuleFn { - name: builtin.to_string(), + name: builtin.to_string().replace("ByteString", "ByteArray"), field_map: None, module: "".to_string(), arity, @@ -325,7 +400,7 @@ impl From for ValueConstructor { builtin: Some(builtin), }, ) - } + }) } pub fn int() -> Arc { diff --git a/crates/uplc/Cargo.toml b/crates/uplc/Cargo.toml index 4e2dcbd2..4f35e214 100644 --- a/crates/uplc/Cargo.toml +++ b/crates/uplc/Cargo.toml @@ -27,6 +27,8 @@ thiserror = "1.0.31" anyhow = "1.0.57" serde = { version = "1.0.144", features = ["derive"] } serde_json = "1.0.85" +strum = "0.24.1" +strum_macros = "0.24.3" [dev-dependencies] hex = "0.4.3" diff --git a/crates/uplc/src/builtins.rs b/crates/uplc/src/builtins.rs index 92872ee9..39eaf625 100644 --- a/crates/uplc/src/builtins.rs +++ b/crates/uplc/src/builtins.rs @@ -1,11 +1,13 @@ use std::{fmt::Display, str::FromStr}; +use strum_macros::EnumIter; + use flat_rs::de; /// All the possible builtin functions in Untyped Plutus Core. #[repr(u8)] #[allow(non_camel_case_types)] -#[derive(Debug, Clone, PartialEq, Eq, Copy)] +#[derive(Debug, Clone, PartialEq, Eq, Copy, EnumIter)] pub enum DefaultFunction { // Integer functions AddInteger = 0, diff --git a/examples/sample/src/sample/context.ak b/examples/sample/src/sample/context.ak new file mode 100644 index 00000000..5a07d7e2 --- /dev/null +++ b/examples/sample/src/sample/context.ak @@ -0,0 +1,8 @@ +pub type ScriptContext(purpose) { + tx_info: TxInfo, + script_purpose: purpose +} + +pub type TxInfo { + idk: Int +} \ No newline at end of file diff --git a/examples/sample/src/sample/mint.ak b/examples/sample/src/sample/mint.ak new file mode 100644 index 00000000..2d54a356 --- /dev/null +++ b/examples/sample/src/sample/mint.ak @@ -0,0 +1,7 @@ +use sample/context + +pub type Mint { + currency_symbol: ByteArray +} + +pub type ScriptContext = context.ScriptContext(Mint) \ No newline at end of file diff --git a/examples/sample/src/sample/spend.ak b/examples/sample/src/sample/spend.ak new file mode 100644 index 00000000..1f11f897 --- /dev/null +++ b/examples/sample/src/sample/spend.ak @@ -0,0 +1,7 @@ +use sample/context + +pub type Spend { + idk: Int +} + +pub type ScriptContext = context.ScriptContext(Spend) \ No newline at end of file diff --git a/examples/sample/src/sample/syntax.ak b/examples/sample/src/sample/syntax.ak deleted file mode 100644 index 283b132e..00000000 --- a/examples/sample/src/sample/syntax.ak +++ /dev/null @@ -1,13 +0,0 @@ -use aiken/builtin.{addInteger} - -pub type ScriptContext { - idk: Int -} - -pub fn append(a: ByteArray, b: ByteArray) -> ByteArray { - todo -} - -pub fn add(a, b) { - addInteger(a, b) -} \ No newline at end of file diff --git a/examples/sample/src/scripts/swap.ak b/examples/sample/src/scripts/swap.ak index 8b395a43..6893941b 100644 --- a/examples/sample/src/scripts/swap.ak +++ b/examples/sample/src/scripts/swap.ak @@ -1,5 +1,5 @@ -use sample -use sample/syntax.{append} +use sample/mint +use sample/spend pub type Datum { something: String, @@ -10,13 +10,7 @@ pub type Redeemer { Sell } -pub fn validate(datum: Datum, rdmr: Redeemer, ctx: sample.ScriptContext) -> Bool { - let thing = if True { - 3 - } else { - "thing" - } - +pub fn spend(datum: Datum, rdmr: Redeemer, ctx: spend.ScriptContext) -> Bool { when rdmr is { Buy -> True Sell -> datum.something == "Aiken"