Allow test definition to carry one parameter

The parameter is special as it takes no annotation but a 'via' keyword followed by an expression that should unify to a Fuzzer<a>, where Fuzzer<a> = fn(Seed) -> (Seed, a). The current commit only allow name identifiers for now. Ultimately, this may allow full expressions.
This commit is contained in:
KtorZ
2024-02-24 19:02:45 +01:00
parent bfb4455e0f
commit aadf3cfb48
13 changed files with 579 additions and 187 deletions

View File

@@ -0,0 +1,50 @@
---
source: crates/aiken-lang/src/parser/definition/test.rs
description: "Code:\n\ntest foo(x via f, y via g) {\n True\n}\n"
---
Test(
Function {
arguments: [
ArgVia {
arg_name: Named {
name: "x",
label: "x",
location: 9..10,
is_validator_param: false,
},
location: 9..16,
via: DefinitionIdentifier {
module: None,
name: "f",
},
tipo: (),
},
ArgVia {
arg_name: Named {
name: "y",
label: "y",
location: 18..19,
is_validator_param: false,
},
location: 18..25,
via: DefinitionIdentifier {
module: None,
name: "g",
},
tipo: (),
},
],
body: Var {
location: 33..37,
name: "True",
},
doc: None,
location: 0..26,
name: "foo",
public: false,
return_annotation: None,
return_type: (),
end_position: 38,
can_error: false,
},
)

View File

@@ -0,0 +1,38 @@
---
source: crates/aiken-lang/src/parser/definition/test.rs
description: "Code:\n\ntest foo(x via fuzz.any_int) {\n True\n}\n"
---
Test(
Function {
arguments: [
ArgVia {
arg_name: Named {
name: "x",
label: "x",
location: 9..10,
is_validator_param: false,
},
location: 9..27,
via: DefinitionIdentifier {
module: Some(
"fuzz",
),
name: "any_int",
},
tipo: (),
},
],
body: Var {
location: 35..39,
name: "True",
},
doc: None,
location: 0..28,
name: "foo",
public: false,
return_annotation: None,
return_type: (),
end_position: 40,
can_error: false,
},
)

View File

@@ -0,0 +1,21 @@
---
source: crates/aiken-lang/src/parser/definition/test.rs
description: "Code:\n\ntest foo() {\n True\n}\n"
---
Test(
Function {
arguments: [],
body: Var {
location: 17..21,
name: "True",
},
doc: None,
location: 0..10,
name: "foo",
public: false,
return_annotation: None,
return_type: (),
end_position: 22,
can_error: false,
},
)

View File

@@ -13,8 +13,12 @@ pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError
.or_not()
.then_ignore(just(Token::Test))
.then(select! {Token::Name {name} => name})
.then_ignore(just(Token::LeftParen))
.then_ignore(just(Token::RightParen))
.then(
via()
.separated_by(just(Token::Comma))
.allow_trailing()
.delimited_by(just(Token::LeftParen), just(Token::RightParen)),
)
.then(just(Token::Fail).ignored().or_not())
.map_with_span(|name, span| (name, span))
.then(
@@ -22,26 +26,72 @@ pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError
.or_not()
.delimited_by(just(Token::LeftBrace), just(Token::RightBrace)),
)
.map_with_span(|((((old_fail, name), fail), span_end), body), span| {
ast::UntypedDefinition::Test(ast::Function {
arguments: vec![],
body: body.unwrap_or_else(|| UntypedExpr::todo(None, span)),
doc: None,
location: span_end,
end_position: span.end - 1,
.map_with_span(
|(((((old_fail, name), arguments), fail), span_end), body), span| {
ast::UntypedDefinition::Test(ast::Function {
arguments,
body: body.unwrap_or_else(|| UntypedExpr::todo(None, span)),
doc: None,
location: span_end,
end_position: span.end - 1,
name,
public: false,
return_annotation: Some(ast::Annotation::boolean(span)),
return_type: (),
can_error: fail.is_some() || old_fail.is_some(),
})
},
)
}
pub fn via() -> impl Parser<Token, ast::UntypedArgVia, Error = ParseError> {
choice((
select! {Token::DiscardName {name} => name}.map_with_span(|name, span| {
ast::ArgName::Discarded {
label: name.clone(),
name,
public: false,
return_annotation: None,
return_type: (),
can_error: fail.is_some() || old_fail.is_some(),
})
})
location: span,
}
}),
select! {Token::Name {name} => name}.map_with_span(move |name, location| {
ast::ArgName::Named {
label: name.clone(),
name,
location,
is_validator_param: false,
}
}),
))
.then_ignore(just(Token::Via))
.then(
select! { Token::Name { name } => name }
.then_ignore(just(Token::Dot))
.or_not(),
)
.then(select! { Token::Name { name } => name })
.map_with_span(|((arg_name, module), name), location| ast::ArgVia {
arg_name,
via: ast::DefinitionIdentifier { module, name },
tipo: (),
location,
})
}
#[cfg(test)]
mod tests {
use crate::assert_definition;
#[test]
fn def_test() {
assert_definition!(
r#"
test foo() {
True
}
"#
);
}
#[test]
fn def_test_fail() {
assert_definition!(
@@ -54,4 +104,26 @@ mod tests {
"#
);
}
#[test]
fn def_property_test() {
assert_definition!(
r#"
test foo(x via fuzz.any_int) {
True
}
"#
);
}
#[test]
fn def_invalid_property_test() {
assert_definition!(
r#"
test foo(x via f, y via g) {
True
}
"#
);
}
}

View File

@@ -240,6 +240,7 @@ pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = ParseError> {
"type" => Token::Type,
"when" => Token::When,
"validator" => Token::Validator,
"via" => Token::Via,
_ => {
if s.chars().next().map_or(false, |c| c.is_uppercase()) {
Token::UpName {

View File

@@ -89,6 +89,7 @@ pub enum Token {
When,
Trace,
Validator,
Via,
}
impl fmt::Display for Token {
@@ -176,6 +177,7 @@ impl fmt::Display for Token {
Token::Test => "test",
Token::Fail => "fail",
Token::Validator => "validator",
Token::Via => "via",
};
write!(f, "\"{s}\"")
}