feat: implement new validator parsing

This commit is contained in:
rvcas
2024-07-18 17:50:36 -04:00
committed by KtorZ
parent c98e32d3e9
commit 0de5cbc74e
3 changed files with 148 additions and 74 deletions

View File

@@ -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
}
}
"#
);
}
}