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
No known key found for this signature in database
GPG Key ID: 33173CB6F77F4277
3 changed files with 148 additions and 74 deletions

View File

@ -541,10 +541,11 @@ pub type UntypedValidator = Validator<(), UntypedArg, UntypedExpr>;
pub struct Validator<T, Arg, Expr> { pub struct Validator<T, Arg, Expr> {
pub doc: Option<String>, pub doc: Option<String>,
pub end_position: usize, pub end_position: usize,
pub fun: Function<T, Expr, Arg>, pub handlers: Vec<Function<T, Expr, Arg>>,
pub other_fun: Option<Function<T, Expr, Arg>>,
pub location: Span, pub location: Span,
pub name: String,
pub params: Vec<Arg>, pub params: Vec<Arg>,
pub fallback: Function<T, Expr, Arg>,
} }
impl TypedValidator { impl TypedValidator {
@ -552,12 +553,12 @@ impl TypedValidator {
self.params self.params
.iter() .iter()
.find_map(|arg| arg.find_node(byte_index)) .find_map(|arg| arg.find_node(byte_index))
.or_else(|| self.fun.find_node(byte_index))
.or_else(|| { .or_else(|| {
self.other_fun self.handlers
.as_ref() .iter()
.and_then(|f| f.find_node(byte_index)) .find_map(|func| func.find_node(byte_index))
}) })
.or_else(|| self.fallback.find_node(byte_index))
} }
pub fn into_function_definition<'a, F>( pub fn into_function_definition<'a, F>(
@ -568,27 +569,28 @@ impl TypedValidator {
where where
F: Fn(&'a TypedFunction, Option<&'a TypedFunction>) -> Option<&'a TypedFunction> + 'a, F: Fn(&'a TypedFunction, Option<&'a TypedFunction>) -> Option<&'a TypedFunction> + 'a,
{ {
match select(&self.fun, self.other_fun.as_ref()) { // match select(&self.fun, self.other_fun.as_ref()) {
None => None, // None => None,
Some(fun) => { // Some(fun) => {
let mut fun = fun.clone(); // let mut fun = fun.clone();
fun.arguments = self // fun.arguments = self
.params // .params
.clone() // .clone()
.into_iter() // .into_iter()
.chain(fun.arguments) // .chain(fun.arguments)
.collect(); // .collect();
Some(( // Some((
FunctionAccessKey { // FunctionAccessKey {
module_name: module_name.to_string(), // module_name: module_name.to_string(),
function_name: fun.name.clone(), // function_name: fun.name.clone(),
}, // },
fun, // fun,
)) // ))
} // }
} // }
todo!()
} }
} }

View File

@ -236,11 +236,11 @@ impl<'comments> Formatter<'comments> {
Definition::Validator(Validator { Definition::Validator(Validator {
end_position, end_position,
fun, handlers,
other_fun, fallback,
params, params,
.. ..
}) => self.definition_validator(params, fun, other_fun, *end_position), }) => self.definition_validator(params, handlers, fallback, *end_position),
Definition::Test(Function { Definition::Test(Function {
name, name,
@ -582,8 +582,8 @@ impl<'comments> Formatter<'comments> {
fn definition_validator<'a>( fn definition_validator<'a>(
&mut self, &mut self,
params: &'a [UntypedArg], params: &'a [UntypedArg],
fun: &'a UntypedFunction, handlers: &'a [UntypedFunction],
other_fun: &'a Option<UntypedFunction>, fallback: &'a UntypedFunction,
end_position: usize, end_position: usize,
) -> Document<'a> { ) -> Document<'a> {
// validator(params) // validator(params)

View File

@ -1,66 +1,117 @@
use chumsky::prelude::*; use chumsky::prelude::*;
use crate::{ use crate::{
ast, ast::{self, ArgBy, ArgName},
parser::{error::ParseError, token::Token}, 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> { pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError> {
just(Token::Validator) just(Token::Validator)
.ignore_then( .ignore_then(select! {Token::Name {name} => name})
function::param(true) .then(
param(true)
.separated_by(just(Token::Comma)) .separated_by(just(Token::Comma))
.allow_trailing() .allow_trailing()
.delimited_by(just(Token::LeftParen), just(Token::RightParen)) .delimited_by(just(Token::LeftParen), just(Token::RightParen))
.map_with_span(|arguments, span| (arguments, span)) .map_with_span(|arguments, span| (arguments, span))
.or_not(), .or_not(),
) )
// so far: validator my_validator(arg1: Whatever)
.then( .then(
function() select! {Token::Name {name} => name}
.then(args_and_body())
.map(|(name, mut function)| {
function.name = name;
function
})
.repeated() .repeated()
.at_least(1) .then(just(Token::Else).ignore_then(args_and_body()).or_not())
.at_most(2) .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)),
.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
})
}),
) )
.map_with_span(|(opt_extra_params, mut functions), span| { .map_with_span(
let (params, params_span) = opt_extra_params.unwrap_or(( |((name, opt_extra_params), (handlers, opt_catch_all)), span| {
vec![], let (params, params_span) = opt_extra_params.unwrap_or((
ast::Span { vec![],
start: 0, ast::Span {
end: span.start + "validator".len(), start: 0,
}, end: span.start + "validator".len(),
)); },
));
let fun = functions ast::UntypedDefinition::Validator(ast::Validator {
.next() doc: None,
.expect("unwrapping safe because there's 'at_least(1)' function"); 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(); pub fn args_and_body() -> impl Parser<Token, ast::UntypedFunction, Error = ParseError> {
param(false)
ast::UntypedDefinition::Validator(ast::Validator { .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, doc: None,
fun,
other_fun,
location: ast::Span { location: ast::Span {
start: span.start, start: span.start,
// capture the span from the optional params end: return_annotation
end: params_span.end, .as_ref()
.map(|l| l.location().end)
.unwrap_or_else(|| args_span.end),
}, },
params,
end_position: span.end - 1, end_position: span.end - 1,
}) name: "temp".to_string(),
}) public: true,
return_annotation,
return_type: (),
on_test_failure: ast::OnTestFailure::FailImmediately,
},
)
} }
#[cfg(test)] #[cfg(test)]
@ -71,8 +122,8 @@ mod tests {
fn validator() { fn validator() {
assert_definition!( assert_definition!(
r#" r#"
validator { validator hello {
fn foo(datum, rdmr, ctx) { spend (datum, rdmr, ctx) {
True True
} }
} }
@ -84,16 +135,37 @@ mod tests {
fn double_validator() { fn double_validator() {
assert_definition!( assert_definition!(
r#" r#"
validator { validator thing {
fn foo(datum, rdmr, ctx) { spend (datum, rdmr, ctx) {
True True
} }
fn bar(rdmr, ctx) { mint (rdmr, ctx) {
True True
} }
} }
"# "#
); );
} }
#[test]
fn fallback() {
assert_definition!(
r#"
validator thing {
spend (datum, rdmr, ctx) {
True
}
mint (rdmr, ctx) {
True
}
else (_) {
fail
}
}
"#
);
}
} }