feat: implement new validator parsing
This commit is contained in:
@@ -1,66 +1,117 @@
|
||||
use chumsky::prelude::*;
|
||||
|
||||
use crate::{
|
||||
ast,
|
||||
parser::{error::ParseError, token::Token},
|
||||
ast::{self, ArgBy, ArgName},
|
||||
expr::UntypedExpr,
|
||||
parser::{annotation, error::ParseError, expr, token::Token},
|
||||
};
|
||||
|
||||
use super::function;
|
||||
use super::function::param;
|
||||
|
||||
pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError> {
|
||||
just(Token::Validator)
|
||||
.ignore_then(
|
||||
function::param(true)
|
||||
.ignore_then(select! {Token::Name {name} => name})
|
||||
.then(
|
||||
param(true)
|
||||
.separated_by(just(Token::Comma))
|
||||
.allow_trailing()
|
||||
.delimited_by(just(Token::LeftParen), just(Token::RightParen))
|
||||
.map_with_span(|arguments, span| (arguments, span))
|
||||
.or_not(),
|
||||
)
|
||||
// so far: validator my_validator(arg1: Whatever)
|
||||
.then(
|
||||
function()
|
||||
select! {Token::Name {name} => name}
|
||||
.then(args_and_body())
|
||||
.map(|(name, mut function)| {
|
||||
function.name = name;
|
||||
|
||||
function
|
||||
})
|
||||
.repeated()
|
||||
.at_least(1)
|
||||
.at_most(2)
|
||||
.delimited_by(just(Token::LeftBrace), just(Token::RightBrace))
|
||||
.map(|defs| {
|
||||
defs.into_iter().map(|def| {
|
||||
let ast::UntypedDefinition::Fn(fun) = def else {
|
||||
unreachable!("It should be a fn definition");
|
||||
};
|
||||
|
||||
fun
|
||||
})
|
||||
}),
|
||||
.then(just(Token::Else).ignore_then(args_and_body()).or_not())
|
||||
.delimited_by(just(Token::LeftBrace), just(Token::RightBrace)),
|
||||
)
|
||||
.map_with_span(|(opt_extra_params, mut functions), span| {
|
||||
let (params, params_span) = opt_extra_params.unwrap_or((
|
||||
vec![],
|
||||
ast::Span {
|
||||
start: 0,
|
||||
end: span.start + "validator".len(),
|
||||
},
|
||||
));
|
||||
.map_with_span(
|
||||
|((name, opt_extra_params), (handlers, opt_catch_all)), span| {
|
||||
let (params, params_span) = opt_extra_params.unwrap_or((
|
||||
vec![],
|
||||
ast::Span {
|
||||
start: 0,
|
||||
end: span.start + "validator".len(),
|
||||
},
|
||||
));
|
||||
|
||||
let fun = functions
|
||||
.next()
|
||||
.expect("unwrapping safe because there's 'at_least(1)' function");
|
||||
ast::UntypedDefinition::Validator(ast::Validator {
|
||||
doc: None,
|
||||
name,
|
||||
handlers,
|
||||
location: ast::Span {
|
||||
start: span.start,
|
||||
// capture the span from the optional params
|
||||
end: params_span.end,
|
||||
},
|
||||
params,
|
||||
end_position: span.end - 1,
|
||||
fallback: opt_catch_all.unwrap_or(ast::Function {
|
||||
arguments: vec![ast::UntypedArg {
|
||||
by: ArgBy::ByName(ArgName::Discarded {
|
||||
name: "_ctx".to_string(),
|
||||
label: "_ctx".to_string(),
|
||||
location: ast::Span::empty(),
|
||||
}),
|
||||
location: ast::Span::empty(),
|
||||
annotation: None,
|
||||
doc: None,
|
||||
is_validator_param: false,
|
||||
}],
|
||||
body: UntypedExpr::fail(None, ast::Span::empty()),
|
||||
doc: None,
|
||||
location: ast::Span::empty(),
|
||||
end_position: span.end - 1,
|
||||
name: "fallback".to_string(),
|
||||
public: true,
|
||||
return_annotation: None,
|
||||
return_type: (),
|
||||
on_test_failure: ast::OnTestFailure::FailImmediately,
|
||||
}),
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
let other_fun = functions.next();
|
||||
|
||||
ast::UntypedDefinition::Validator(ast::Validator {
|
||||
pub fn args_and_body() -> impl Parser<Token, ast::UntypedFunction, Error = ParseError> {
|
||||
param(false)
|
||||
.separated_by(just(Token::Comma))
|
||||
.allow_trailing()
|
||||
.delimited_by(just(Token::LeftParen), just(Token::RightParen))
|
||||
.map_with_span(|arguments, span| (arguments, span))
|
||||
.then(just(Token::RArrow).ignore_then(annotation()).or_not())
|
||||
.then(
|
||||
expr::sequence()
|
||||
.or_not()
|
||||
.delimited_by(just(Token::LeftBrace), just(Token::RightBrace)),
|
||||
)
|
||||
.map_with_span(
|
||||
|(((arguments, args_span), return_annotation), body), span| ast::Function {
|
||||
arguments,
|
||||
body: body.unwrap_or_else(|| UntypedExpr::todo(None, span)),
|
||||
doc: None,
|
||||
fun,
|
||||
other_fun,
|
||||
location: ast::Span {
|
||||
start: span.start,
|
||||
// capture the span from the optional params
|
||||
end: params_span.end,
|
||||
end: return_annotation
|
||||
.as_ref()
|
||||
.map(|l| l.location().end)
|
||||
.unwrap_or_else(|| args_span.end),
|
||||
},
|
||||
params,
|
||||
end_position: span.end - 1,
|
||||
})
|
||||
})
|
||||
name: "temp".to_string(),
|
||||
public: true,
|
||||
return_annotation,
|
||||
return_type: (),
|
||||
on_test_failure: ast::OnTestFailure::FailImmediately,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -71,8 +122,8 @@ mod tests {
|
||||
fn validator() {
|
||||
assert_definition!(
|
||||
r#"
|
||||
validator {
|
||||
fn foo(datum, rdmr, ctx) {
|
||||
validator hello {
|
||||
spend (datum, rdmr, ctx) {
|
||||
True
|
||||
}
|
||||
}
|
||||
@@ -84,16 +135,37 @@ mod tests {
|
||||
fn double_validator() {
|
||||
assert_definition!(
|
||||
r#"
|
||||
validator {
|
||||
fn foo(datum, rdmr, ctx) {
|
||||
validator thing {
|
||||
spend (datum, rdmr, ctx) {
|
||||
True
|
||||
}
|
||||
|
||||
fn bar(rdmr, ctx) {
|
||||
mint (rdmr, ctx) {
|
||||
True
|
||||
}
|
||||
}
|
||||
"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fallback() {
|
||||
assert_definition!(
|
||||
r#"
|
||||
validator thing {
|
||||
spend (datum, rdmr, ctx) {
|
||||
True
|
||||
}
|
||||
|
||||
mint (rdmr, ctx) {
|
||||
True
|
||||
}
|
||||
|
||||
else (_) {
|
||||
fail
|
||||
}
|
||||
}
|
||||
"#
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user