Authorize complete patterns as function args.

This is mainly a syntactic trick/sugar, but it's been pretty annoying
  to me for a while that we can't simply pattern-match/destructure
  single-variant constructors directly from the args list. A classic
  example is when writing property tests:

  ```ak
  test foo(params via both(bytearray(), int())) {
    let (bytes, ix) = params
    ...
  }
  ```

  Now can be replaced simply with:

  ```
  test foo((bytes, ix) via both(bytearray(), int())) {
    ...
  }
  ```

  If feels natural, especially coming from the JavaScript, Haskell or
  Rust worlds and is mostly convenient. Behind the scene, the compiler
  does nothing more than re-writing the AST as the first form, with
  pre-generated arg names. Then, we fully rely on the existing
  type-checking capabilities and thus, works in a seamless way as if we
  were just pattern matching inline.
This commit is contained in:
KtorZ 2024-06-07 15:23:33 +02:00
parent b6da42baf2
commit 858dfccc82
No known key found for this signature in database
GPG Key ID: 33173CB6F77F4277
23 changed files with 944 additions and 68 deletions

View File

@ -2,6 +2,10 @@
## v1.0.30-alpha - UNRELEASED
### Added
- **aiken-lang**: also authorize (complete) patterns in function arguments list instead of only variable names. @KtorZ
### Changed
- **aiken-lang**: duplicate import lines are now automatically merged instead of raising a warning. However, imports can no longer appear anywhere in the file and must come as the first definitions. @KtorZ

View File

@ -15,7 +15,7 @@ use std::{
rc::Rc,
};
use uplc::machine::runtime::Compressable;
use vec1::Vec1;
use vec1::{vec1, Vec1};
pub const BACKPASS_VARIABLE: &str = "_backpass";
pub const CAPTURE_VARIABLE: &str = "_capture";
@ -797,6 +797,32 @@ pub enum ArgBy {
ByPattern(UntypedPattern),
}
impl ArgBy {
pub fn into_extra_assignment(
self,
name: &ArgName,
annotation: Option<&Annotation>,
location: Span,
) -> Option<UntypedExpr> {
match self {
ArgBy::ByName(..) => None,
ArgBy::ByPattern(pattern) => Some(UntypedExpr::Assignment {
location,
value: Box::new(UntypedExpr::Var {
location,
name: name.get_name(),
}),
patterns: vec1![AssignmentPattern {
pattern,
location,
annotation: annotation.cloned(),
}],
kind: AssignmentKind::Let { backpassing: false },
}),
}
}
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct UntypedArg {
pub by: ArgBy,

View File

@ -464,15 +464,17 @@ impl<'comments> Formatter<'comments> {
let doc_comments = self.doc_comments(arg.location.start);
let doc = match arg.by {
ArgBy::ByName(ref arg_name) => match &arg.annotation {
None => arg_name.to_doc(),
Some(a) => arg_name.to_doc().append(": ").append(self.annotation(a)),
}
.group(),
ArgBy::ByPattern(..) => todo!(),
let mut doc = match arg.by {
ArgBy::ByName(ref arg_name) => arg_name.to_doc(),
ArgBy::ByPattern(ref pattern) => self.pattern(pattern),
};
doc = match &arg.annotation {
None => doc,
Some(a) => doc.append(": ").append(self.annotation(a)),
}
.group();
let doc = doc_comments.append(doc.group()).group();
commented(doc, comments)
@ -483,12 +485,14 @@ impl<'comments> Formatter<'comments> {
let doc_comments = self.doc_comments(arg_via.arg.location.start);
let doc = match arg_via.arg.by {
ArgBy::ByName(ref arg_name) => match &arg_via.arg.annotation {
None => arg_name.to_doc(),
Some(a) => arg_name.to_doc().append(": ").append(self.annotation(a)),
},
ArgBy::ByPattern(..) => todo!(),
let mut doc = match arg_via.arg.by {
ArgBy::ByName(ref arg_name) => arg_name.to_doc(),
ArgBy::ByPattern(ref pattern) => self.pattern(pattern),
};
doc = match &arg_via.arg.annotation {
None => doc,
Some(a) => doc.append(": ").append(self.annotation(a)),
}
.append(" via ")
.append(self.expr(&arg_via.via, false))
@ -1981,7 +1985,11 @@ impl<'a> Documentable<'a> for &'a ArgName {
}
fn pub_(public: bool) -> Document<'static> {
if public { "pub ".to_doc() } else { nil() }
if public {
"pub ".to_doc()
} else {
nil()
}
}
impl<'a> Documentable<'a> for &'a UnqualifiedImport {

View File

@ -1,8 +1,7 @@
use crate::{
ast,
ast::ArgBy::ByName,
expr::UntypedExpr,
parser::{annotation, error::ParseError, expr, token::Token, utils},
parser::{annotation, error::ParseError, expr, pattern, token::Token, utils},
};
use chumsky::prelude::*;
@ -51,38 +50,45 @@ pub fn param(is_validator_param: bool) -> impl Parser<Token, ast::UntypedArg, Er
choice((
select! {Token::Name {name} => name}
.then(select! {Token::DiscardName {name} => name})
.map_with_span(|(label, name), span| ast::ArgName::Discarded {
label,
name,
location: span,
.map_with_span(|(label, name), span| {
ast::ArgBy::ByName(ast::ArgName::Discarded {
label,
name,
location: span,
})
}),
select! {Token::DiscardName {name} => name}.map_with_span(|name, span| {
ast::ArgName::Discarded {
ast::ArgBy::ByName(ast::ArgName::Discarded {
label: name.clone(),
name,
location: span,
}
})
}),
select! {Token::Name {name} => name}
.then(select! {Token::Name {name} => name})
.map_with_span(|(label, name), span| ast::ArgName::Named {
label,
.map_with_span(|(label, name), span| {
ast::ArgBy::ByName(ast::ArgName::Named {
label,
name,
location: span,
})
}),
select! {Token::Name {name} => name}.map_with_span(|name, span| {
ast::ArgBy::ByName(ast::ArgName::Named {
label: name.clone(),
name,
location: span,
}),
select! {Token::Name {name} => name}.map_with_span(|name, span| ast::ArgName::Named {
label: name.clone(),
name,
location: span,
})
}),
pattern().map(ast::ArgBy::ByPattern),
))
.then(just(Token::Colon).ignore_then(annotation()).or_not())
.map_with_span(move |(arg_name, annotation), span| ast::UntypedArg {
.map_with_span(move |(by, annotation), span| ast::UntypedArg {
location: span,
annotation,
doc: None,
is_validator_param,
by: ByName(arg_name),
by,
})
}
@ -118,4 +124,36 @@ mod tests {
"#
);
}
#[test]
fn function_by_pattern_no_annotation() {
assert_definition!(
r#"
fn foo(Foo { my_field }) {
my_field * 2
}
"#
);
}
#[test]
fn function_by_pattern_with_annotation() {
assert_definition!(
r#"
fn foo(Foo { my_field }: Foo) {
my_field * 2
}
"#
);
}
#[test]
fn function_by_pattern_with_alias() {
assert_definition!(
r#"
fn foo(Foo { my_field, .. } as x) {
my_field * x.my_other_field
}
"#
);
}
}

View File

@ -0,0 +1,62 @@
---
source: crates/aiken-lang/src/parser/definition/function.rs
description: "Code:\n\nfn foo(Foo { my_field }) {\n my_field * 2\n}\n"
---
Fn(
Function {
arguments: [
UntypedArg {
by: ByPattern(
Constructor {
is_record: true,
location: 7..23,
name: "Foo",
arguments: [
CallArg {
label: Some(
"my_field",
),
location: 13..21,
value: Var {
location: 13..21,
name: "my_field",
},
},
],
module: None,
constructor: (),
spread_location: None,
tipo: (),
},
),
location: 7..23,
annotation: None,
doc: None,
is_validator_param: false,
},
],
body: BinOp {
location: 31..43,
name: MultInt,
left: Var {
location: 31..39,
name: "my_field",
},
right: UInt {
location: 42..43,
value: "2",
base: Decimal {
numeric_underscore: false,
},
},
},
doc: None,
location: 0..24,
name: "foo",
public: false,
return_annotation: None,
return_type: (),
end_position: 44,
on_test_failure: FailImmediately,
},
)

View File

@ -0,0 +1,69 @@
---
source: crates/aiken-lang/src/parser/definition/function.rs
description: "Code:\n\nfn foo(Foo { my_field, .. } as x) {\n my_field * x.my_other_field\n}\n"
---
Fn(
Function {
arguments: [
UntypedArg {
by: ByPattern(
Assign {
name: "x",
location: 7..32,
pattern: Constructor {
is_record: true,
location: 7..27,
name: "Foo",
arguments: [
CallArg {
label: Some(
"my_field",
),
location: 13..21,
value: Var {
location: 13..21,
name: "my_field",
},
},
],
module: None,
constructor: (),
spread_location: Some(
23..25,
),
tipo: (),
},
},
),
location: 7..32,
annotation: None,
doc: None,
is_validator_param: false,
},
],
body: BinOp {
location: 40..67,
name: MultInt,
left: Var {
location: 40..48,
name: "my_field",
},
right: FieldAccess {
location: 51..67,
label: "my_other_field",
container: Var {
location: 51..52,
name: "x",
},
},
},
doc: None,
location: 0..33,
name: "foo",
public: false,
return_annotation: None,
return_type: (),
end_position: 68,
on_test_failure: FailImmediately,
},
)

View File

@ -0,0 +1,69 @@
---
source: crates/aiken-lang/src/parser/definition/function.rs
description: "Code:\n\nfn foo(Foo { my_field }: Foo) {\n my_field * 2\n}\n"
---
Fn(
Function {
arguments: [
UntypedArg {
by: ByPattern(
Constructor {
is_record: true,
location: 7..23,
name: "Foo",
arguments: [
CallArg {
label: Some(
"my_field",
),
location: 13..21,
value: Var {
location: 13..21,
name: "my_field",
},
},
],
module: None,
constructor: (),
spread_location: None,
tipo: (),
},
),
location: 7..28,
annotation: Some(
Constructor {
location: 25..28,
module: None,
name: "Foo",
arguments: [],
},
),
doc: None,
is_validator_param: false,
},
],
body: BinOp {
location: 36..48,
name: MultInt,
left: Var {
location: 36..44,
name: "my_field",
},
right: UInt {
location: 47..48,
value: "2",
base: Decimal {
numeric_underscore: false,
},
},
},
doc: None,
location: 0..29,
name: "foo",
public: false,
return_annotation: None,
return_type: (),
end_position: 49,
on_test_failure: FailImmediately,
},
)

View File

@ -7,6 +7,7 @@ use crate::{
chain::{call::parser as call, field_access, tuple_index::parser as tuple_index, Chain},
error::ParseError,
expr::{self, bytearray, int as uint, list, string, tuple, var},
pattern,
token::Token,
},
};
@ -54,25 +55,28 @@ pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError
pub fn via() -> impl Parser<Token, ast::UntypedArgVia, Error = ParseError> {
choice((
select! {Token::DiscardName {name} => name}.map_with_span(|name, span| {
ast::ArgName::Discarded {
ast::ArgBy::ByName(ast::ArgName::Discarded {
label: name.clone(),
name,
location: span,
}
})
}),
select! {Token::Name {name} => name}.map_with_span(|name, location| ast::ArgName::Named {
label: name.clone(),
name,
location,
select! {Token::Name {name} => name}.map_with_span(|name, location| {
ast::ArgBy::ByName(ast::ArgName::Named {
label: name.clone(),
name,
location,
})
}),
pattern().map(ast::ArgBy::ByPattern),
))
.then(just(Token::Colon).ignore_then(annotation()).or_not())
.map_with_span(|(arg_name, annotation), location| (arg_name, annotation, location))
.then_ignore(just(Token::Via))
.then(fuzzer())
.map(|((arg_name, annotation, location), via)| ast::ArgVia {
.map(|((by, annotation, location), via)| ast::ArgVia {
arg: ast::UntypedArg {
by: ast::ArgBy::ByName(arg_name),
by,
annotation,
location,
doc: None,

View File

@ -1,7 +1,7 @@
use crate::{
ast,
expr::{FnStyle, UntypedExpr},
parser::{annotation, error::ParseError, token::Token},
parser::{annotation, error::ParseError, pattern, token::Token},
};
use chumsky::prelude::*;
@ -32,25 +32,28 @@ pub fn params() -> impl Parser<Token, ast::UntypedArg, Error = ParseError> {
// TODO: return a better error when a label is provided `UnexpectedLabel`
choice((
select! {Token::DiscardName {name} => name}.map_with_span(|name, span| {
ast::ArgName::Discarded {
ast::ArgBy::ByName(ast::ArgName::Discarded {
label: name.clone(),
name,
location: span,
}
})
}),
select! {Token::Name {name} => name}.map_with_span(|name, span| ast::ArgName::Named {
label: name.clone(),
name,
location: span,
select! {Token::Name {name} => name}.map_with_span(|name, span| {
ast::ArgBy::ByName(ast::ArgName::Named {
label: name.clone(),
name,
location: span,
})
}),
pattern().map(ast::ArgBy::ByPattern),
))
.then(just(Token::Colon).ignore_then(annotation()).or_not())
.map_with_span(|(arg_name, annotation), span| ast::UntypedArg {
.map_with_span(|(by, annotation), span| ast::UntypedArg {
is_validator_param: false,
location: span,
annotation,
doc: None,
by: ast::ArgBy::ByName(arg_name),
by,
})
}
@ -62,4 +65,19 @@ mod tests {
fn anonymous_function_basic() {
assert_expr!(r#"fn (a: Int) -> Int { a + 1 }"#);
}
#[test]
fn anonymous_function_by_pattern_no_annotation() {
assert_expr!(r#"fn (Foo { my_field }) { my_field * 2 }"#);
}
#[test]
fn anonymous_function_by_pattern_with_annotation() {
assert_expr!(r#"fn (Foo { my_field } : Foo) { my_field * 2 }"#);
}
#[test]
fn anonymous_function_by_pattern_with_alias() {
assert_expr!(r#"fn (Foo { my_field, .. } as x) { my_field * my_other_field }"#);
}
}

View File

@ -0,0 +1,55 @@
---
source: crates/aiken-lang/src/parser/expr/anonymous_function.rs
description: "Code:\n\nfn (Foo { my_field }) { my_field * 2 }"
---
Fn {
location: 0..38,
fn_style: Plain,
arguments: [
UntypedArg {
by: ByPattern(
Constructor {
is_record: true,
location: 4..20,
name: "Foo",
arguments: [
CallArg {
label: Some(
"my_field",
),
location: 10..18,
value: Var {
location: 10..18,
name: "my_field",
},
},
],
module: None,
constructor: (),
spread_location: None,
tipo: (),
},
),
location: 4..20,
annotation: None,
doc: None,
is_validator_param: false,
},
],
body: BinOp {
location: 24..36,
name: MultInt,
left: Var {
location: 24..32,
name: "my_field",
},
right: UInt {
location: 35..36,
value: "2",
base: Decimal {
numeric_underscore: false,
},
},
},
return_annotation: None,
}

View File

@ -0,0 +1,58 @@
---
source: crates/aiken-lang/src/parser/expr/anonymous_function.rs
description: "Code:\n\nfn (Foo { my_field, .. } as x) { my_field * my_other_field }"
---
Fn {
location: 0..60,
fn_style: Plain,
arguments: [
UntypedArg {
by: ByPattern(
Assign {
name: "x",
location: 4..29,
pattern: Constructor {
is_record: true,
location: 4..24,
name: "Foo",
arguments: [
CallArg {
label: Some(
"my_field",
),
location: 10..18,
value: Var {
location: 10..18,
name: "my_field",
},
},
],
module: None,
constructor: (),
spread_location: Some(
20..22,
),
tipo: (),
},
},
),
location: 4..29,
annotation: None,
doc: None,
is_validator_param: false,
},
],
body: BinOp {
location: 33..58,
name: MultInt,
left: Var {
location: 33..41,
name: "my_field",
},
right: Var {
location: 44..58,
name: "my_other_field",
},
},
return_annotation: None,
}

View File

@ -0,0 +1,62 @@
---
source: crates/aiken-lang/src/parser/expr/anonymous_function.rs
description: "Code:\n\nfn (Foo { my_field } : Foo) { my_field * 2 }"
---
Fn {
location: 0..44,
fn_style: Plain,
arguments: [
UntypedArg {
by: ByPattern(
Constructor {
is_record: true,
location: 4..20,
name: "Foo",
arguments: [
CallArg {
label: Some(
"my_field",
),
location: 10..18,
value: Var {
location: 10..18,
name: "my_field",
},
},
],
module: None,
constructor: (),
spread_location: None,
tipo: (),
},
),
location: 4..26,
annotation: Some(
Constructor {
location: 23..26,
module: None,
name: "Foo",
arguments: [],
},
),
doc: None,
is_validator_param: false,
},
],
body: BinOp {
location: 30..42,
name: MultInt,
left: Var {
location: 30..38,
name: "my_field",
},
right: UInt {
location: 41..42,
value: "2",
base: Decimal {
numeric_underscore: false,
},
},
},
return_annotation: None,
}

View File

@ -2539,3 +2539,37 @@ fn mutually_recursive_1() {
assert!(check(parse(source_code)).is_ok());
}
#[test]
fn fn_single_variant_pattern() {
let source_code = r#"
pub type Foo {
a: Int
}
pub fn foo(Foo { a }) {
a + 1
}
"#;
assert!(dbg!(check(parse(source_code))).is_ok());
}
#[test]
fn fn_multi_variant_pattern() {
let source_code = r#"
type Foo {
A { a: Int }
B { b: Int }
}
pub fn foo(A { a }) {
a + 1
}
"#;
assert!(matches!(
dbg!(check_validator(parse(source_code))),
Err((_, Error::NotExhaustivePatternMatch { .. }))
))
}

View File

@ -883,3 +883,68 @@ fn format_pairs() {
}"#
);
}
#[test]
fn format_fn_pattern() {
assert_format!(
r#"
pub fn foo(Foo { a, b, .. }) {
todo
}
pub fn bar([Bar] : List<Bar>) {
todo
}
pub fn baz((Baz, Baz) as x) {
todo
}
pub fn fiz(Pair(fst, snd) as x: Pair<Int, Int>) {
todo
}
test buz((a, b) via some_fuzzer()) {
todo
}
"#
);
}
#[test]
fn format_anon_fn_pattern() {
assert_format!(
r#"
pub fn main() {
let foo = fn (Foo { a, b, .. }) { todo }
let bar = fn ([Bar] : List<Bar>) { todo }
let baz = fn ((Baz, Baz) as x) { todo }
let fiz = fn (Pair(fst, snd) as x: Pair<Int, Int>) { todo }
todo
}
"#
);
}
#[test]
fn format_validator_pattern() {
assert_format!(
r#"
validator(Foo { a, b, .. }) {
fn foo() { todo }
}
validator([Bar] : List<Bar>) {
fn bar() { todo }
}
validator((Baz, Baz) as x) {
fn baz() { todo }
}
validator((fst, snd) as x: Pair<Int, Int>) {
fn fiz() { todo }
}
"#
);
}

View File

@ -0,0 +1,23 @@
---
source: crates/aiken-lang/src/tests/format.rs
description: "Code:\n\npub fn main() {\n let foo = fn (Foo { a, b, .. }) { todo }\n let bar = fn ([Bar] : List<Bar>) { todo }\n let baz = fn ((Baz, Baz) as x) { todo }\n let fiz = fn (Pair(fst, snd) as x: Pair<Int, Int>) { todo }\n todo\n}\n"
---
pub fn main() {
let foo =
fn(Foo { a, b, .. }) {
todo
}
let bar =
fn([Bar]: List<Bar>) {
todo
}
let baz =
fn((Baz, Baz) as x) {
todo
}
let fiz =
fn(Pair(fst, snd) as x: Pair<Int, Int>) {
todo
}
todo
}

View File

@ -0,0 +1,23 @@
---
source: crates/aiken-lang/src/tests/format.rs
description: "Code:\n\npub fn foo(Foo { a, b, .. }) {\n todo\n}\n\npub fn bar([Bar] : List<Bar>) {\n todo\n}\n\npub fn baz((Baz, Baz) as x) {\n todo\n}\n\npub fn fiz(Pair(fst, snd) as x: Pair<Int, Int>) {\n todo\n}\n\ntest buz((a, b) via some_fuzzer()) {\n todo\n}\n"
---
pub fn foo(Foo { a, b, .. }) {
todo
}
pub fn bar([Bar]: List<Bar>) {
todo
}
pub fn baz((Baz, Baz) as x) {
todo
}
pub fn fiz(Pair(fst, snd) as x: Pair<Int, Int>) {
todo
}
test buz((a, b) via some_fuzzer()) {
todo
}

View File

@ -0,0 +1,27 @@
---
source: crates/aiken-lang/src/tests/format.rs
description: "Code:\n\nvalidator(Foo { a, b, .. }) {\n fn foo() { todo }\n}\n\nvalidator([Bar] : List<Bar>) {\n fn bar() { todo }\n}\n\nvalidator((Baz, Baz) as x) {\n fn baz() { todo }\n}\n\nvalidator((fst, snd) as x: Pair<Int, Int>) {\n fn fiz() { todo }\n}\n"
---
validator(Foo { a, b, .. }) {
fn foo() {
todo
}
}
validator([Bar]: List<Bar>) {
fn bar() {
todo
}
}
validator((Baz, Baz) as x) {
fn baz() {
todo
}
}
validator((fst, snd) as x: Pair<Int, Int>) {
fn fiz() {
todo
}
}

View File

@ -59,6 +59,39 @@ pub(crate) fn infer_function(
return_type: _,
} = fun;
let mut extra_let_assignments = Vec::new();
for (i, arg) in arguments.iter().enumerate() {
let let_assignment = arg.by.clone().into_extra_assignment(
&arg.arg_name(i),
arg.annotation.as_ref(),
arg.location,
);
match let_assignment {
None => {}
Some(expr) => extra_let_assignments.push(expr),
}
}
let sequence;
let body = if extra_let_assignments.is_empty() {
body
} else if let UntypedExpr::Sequence { expressions, .. } = body {
extra_let_assignments.extend(expressions.clone());
sequence = UntypedExpr::Sequence {
expressions: extra_let_assignments,
location: *location,
};
&sequence
} else {
extra_let_assignments.extend([body.clone()]);
sequence = UntypedExpr::Sequence {
expressions: extra_let_assignments,
location: body.location(),
};
&sequence
};
let preregistered_fn = environment
.get_variable(name)
.expect("Could not find preregistered type for function");
@ -331,9 +364,13 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
let mut arguments = Vec::new();
let mut extra_let_assignments = Vec::new();
for (i, arg) in args.into_iter().enumerate() {
let arg = self.infer_param(arg, expected_args.get(i).cloned(), i)?;
let (arg, extra_let_assignment) =
self.infer_param(arg, expected_args.get(i).cloned(), i)?;
if let Some(expr) = extra_let_assignment {
extra_let_assignments.push(expr);
}
arguments.push(arg);
}
@ -342,6 +379,28 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
None => None,
};
let body_location = body.location();
let body = if extra_let_assignments.is_empty() {
body
} else if let UntypedExpr::Sequence {
location,
expressions,
} = body
{
extra_let_assignments.extend(expressions);
UntypedExpr::Sequence {
expressions: extra_let_assignments,
location,
}
} else {
extra_let_assignments.extend([body]);
UntypedExpr::Sequence {
expressions: extra_let_assignments,
location: body_location,
}
};
self.infer_fn_with_known_types(arguments, body, return_type)
}
@ -1071,13 +1130,12 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
})
}
// TODO: Handle arg pattern
fn infer_param(
&mut self,
untyped_arg: UntypedArg,
expected: Option<Rc<Type>>,
ix: usize,
) -> Result<TypedArg, Error> {
) -> Result<(TypedArg, Option<UntypedExpr>), Error> {
let arg_name = untyped_arg.arg_name(ix);
let UntypedArg {
@ -1102,14 +1160,18 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
self.unify(expected, tipo.clone(), location, false)?;
}
Ok(TypedArg {
let extra_assignment = by.into_extra_assignment(&arg_name, annotation.as_ref(), location);
let typed_arg = TypedArg {
arg_name,
location,
annotation,
tipo,
is_validator_param,
doc,
})
};
Ok((typed_arg, extra_assignment))
}
fn infer_assignment(

View File

@ -507,10 +507,8 @@ impl Prng {
fn as_prng(cst: &PlutusData) -> Prng {
if let PlutusData::Constr(Constr { tag, fields, .. }) = cst {
if *tag == 121 + Prng::SEEDED {
if let [
PlutusData::BoundedBytes(bytes),
PlutusData::BoundedBytes(choices),
] = &fields[..]
if let [PlutusData::BoundedBytes(bytes), PlutusData::BoundedBytes(choices)] =
&fields[..]
{
return Prng::Seeded {
choices: choices.to_vec(),
@ -1089,11 +1087,9 @@ impl TryFrom<TypedExpr> for Assertion<TypedExpr> {
final_else,
..
} => {
if let [
IfBranch {
condition, body, ..
},
] = &branches[..]
if let [IfBranch {
condition, body, ..
}] = &branches[..]
{
let then_is_true = match body {
TypedExpr::Var {
@ -1512,14 +1508,13 @@ mod test {
}
"#});
assert!(
prop.run::<()>(
assert!(prop
.run::<()>(
42,
PropertyTest::DEFAULT_MAX_SUCCESS,
&PlutusVersion::default()
)
.is_success()
);
.is_success());
}
#[test]

View File

@ -0,0 +1,28 @@
# This file was generated by Aiken
# You typically do not need to edit this file
[[requirements]]
name = "aiken-lang/stdlib"
version = "main"
source = "github"
[[requirements]]
name = "aiken-lang/fuzz"
version = "main"
source = "github"
[[packages]]
name = "aiken-lang/stdlib"
version = "main"
requirements = []
source = "github"
[[packages]]
name = "aiken-lang/fuzz"
version = "main"
requirements = []
source = "github"
[etags]
"aiken-lang/fuzz@main" = [{ secs_since_epoch = 1717767691, nanos_since_epoch = 206091000 }, "98cf81aa68f9ccf68bc5aba9be06d06cb1db6e8eff60b668ed5e8ddf3588206b"]
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1717767690, nanos_since_epoch = 920449000 }, "a746f5b5cd3c2ca5dc19c43bcfc64230c546fafea2ba5f8e340c227b85886078"]

View File

@ -0,0 +1,21 @@
name = "aiken-lang/104"
version = "0.0.0"
compiler = "v1.0.29-alpha"
plutus = "v2"
license = "Apache-2.0"
description = "Aiken contracts for project 'aiken-lang/104'"
[repository]
user = "aiken-lang"
project = "104"
platform = "github"
[[dependencies]]
name = "aiken-lang/stdlib"
version = "main"
source = "github"
[[dependencies]]
name = "aiken-lang/fuzz"
version = "main"
source = "github"

View File

@ -0,0 +1,80 @@
{
"preamble": {
"title": "aiken-lang/104",
"description": "Aiken contracts for project 'aiken-lang/104'",
"version": "0.0.0",
"plutusVersion": "v2",
"compiler": {
"name": "Aiken",
"version": "v1.0.29-alpha+257bd23"
},
"license": "Apache-2.0"
},
"validators": [
{
"title": "tests.foo_3",
"redeemer": {
"title": "_data",
"schema": {
"$ref": "#/definitions/Data"
}
},
"parameters": [
{
"title": "th_arg",
"schema": {
"$ref": "#/definitions/tests~1Foo"
}
}
],
"compiledCode": "582401000032323222253330043370e6eb4c018c014dd5001a400429309b2b2b9a5573cae841",
"hash": "047dafbc61fb4a550a28398bde3680c48ff2000cf1022efc883124cd"
}
],
"definitions": {
"Bool": {
"title": "Bool",
"anyOf": [
{
"title": "False",
"dataType": "constructor",
"index": 0,
"fields": []
},
{
"title": "True",
"dataType": "constructor",
"index": 1,
"fields": []
}
]
},
"Data": {
"title": "Data",
"description": "Any Plutus data."
},
"Int": {
"dataType": "integer"
},
"tests/Foo": {
"title": "Foo",
"anyOf": [
{
"title": "Foo",
"dataType": "constructor",
"index": 0,
"fields": [
{
"title": "a0",
"$ref": "#/definitions/Int"
},
{
"title": "a1",
"$ref": "#/definitions/Bool"
}
]
}
]
}
}
}

View File

@ -0,0 +1,45 @@
use aiken/fuzz
type Foo {
a0: Int,
a1: Bool,
}
fn foo_1(Foo { a0, .. }) -> Int {
a0 + 1
}
fn foo_2(Foo { a0, a1 } as foo) -> Int {
if a1 {
a0 + 1
} else {
foo.a0 - 1
}
}
validator(Foo { a0, .. }: Foo) {
fn foo_3(_data, _redeemer) {
a0 == 1
}
}
test example_1() {
foo_1(Foo { a0: 1, a1: False }) == 2
}
test example_2() {
foo_2(Foo { a0: 1, a1: False }) == 0
}
test example_3() {
foo_3(Foo { a0: 1, a1: False }, "", "")
}
test example_4() {
let foo_4 = fn(Foo { a1, .. }) { a1 }
foo_4(Foo { a0: 1, a1: True })
}
test example_5((a, b) via fuzz.both(fuzz.int(), fuzz.int())) {
a + b == b + a
}