feat(definitions):
* add parsing for new validator defs * start adding typechecking * add a unit test for parsing
This commit is contained in:
parent
dfe240ad64
commit
2e7fe191db
|
@ -258,6 +258,18 @@ pub struct ModuleConstant<T, ConstantRecordTag> {
|
|||
pub tipo: T,
|
||||
}
|
||||
|
||||
pub type TypedValidator = Validator<Arc<Type>, TypedExpr>;
|
||||
pub type UntypedValidator = Validator<(), UntypedExpr>;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Validator<T, Expr> {
|
||||
pub doc: Option<String>,
|
||||
pub end_position: usize,
|
||||
pub function: Function<T, Expr>,
|
||||
pub location: Span,
|
||||
pub params: Vec<Arg<T>>,
|
||||
}
|
||||
|
||||
pub type TypedDefinition = Definition<Arc<Type>, TypedExpr, String, String>;
|
||||
pub type UntypedDefinition = Definition<(), UntypedExpr, (), ()>;
|
||||
|
||||
|
@ -274,6 +286,8 @@ pub enum Definition<T, Expr, ConstantRecordTag, PackageName> {
|
|||
ModuleConstant(ModuleConstant<T, ConstantRecordTag>),
|
||||
|
||||
Test(Function<T, Expr>),
|
||||
|
||||
Validator(Validator<T, Expr>),
|
||||
}
|
||||
|
||||
impl<A, B, C, E> Definition<A, B, C, E> {
|
||||
|
@ -284,6 +298,7 @@ impl<A, B, C, E> Definition<A, B, C, E> {
|
|||
| Definition::TypeAlias(TypeAlias { location, .. })
|
||||
| Definition::DataType(DataType { location, .. })
|
||||
| Definition::ModuleConstant(ModuleConstant { location, .. })
|
||||
| Definition::Validator(Validator { location, .. })
|
||||
| Definition::Test(Function { location, .. }) => *location,
|
||||
}
|
||||
}
|
||||
|
@ -295,6 +310,7 @@ impl<A, B, C, E> Definition<A, B, C, E> {
|
|||
| Definition::TypeAlias(TypeAlias { doc, .. })
|
||||
| Definition::DataType(DataType { doc, .. })
|
||||
| Definition::ModuleConstant(ModuleConstant { doc, .. })
|
||||
| Definition::Validator(Validator { doc, .. })
|
||||
| Definition::Test(Function { doc, .. }) => {
|
||||
let _ = std::mem::replace(doc, Some(new_doc));
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ use crate::{
|
|||
Definition, Function, IfBranch, ModuleConstant, Pattern, RecordConstructor,
|
||||
RecordConstructorArg, RecordUpdateSpread, Span, TraceKind, TypeAlias, TypedArg,
|
||||
TypedConstant, UnOp, UnqualifiedImport, UntypedArg, UntypedClause, UntypedClauseGuard,
|
||||
UntypedDefinition, UntypedModule, UntypedPattern, UntypedRecordUpdateArg, Use,
|
||||
UntypedDefinition, UntypedModule, UntypedPattern, UntypedRecordUpdateArg, Use, Validator,
|
||||
CAPTURE_VARIABLE,
|
||||
},
|
||||
docvec,
|
||||
|
@ -236,6 +236,13 @@ impl<'comments> Formatter<'comments> {
|
|||
*end_position,
|
||||
),
|
||||
|
||||
Definition::Validator(Validator {
|
||||
end_position,
|
||||
function,
|
||||
params,
|
||||
..
|
||||
}) => self.definition_validator(params, function, *end_position),
|
||||
|
||||
Definition::Test(Function {
|
||||
name,
|
||||
arguments: args,
|
||||
|
@ -550,6 +557,59 @@ impl<'comments> Formatter<'comments> {
|
|||
.append("}")
|
||||
}
|
||||
|
||||
fn definition_validator<'a>(
|
||||
&mut self,
|
||||
params: &'a [UntypedArg],
|
||||
function: &'a UntypedFunction,
|
||||
end_position: usize,
|
||||
) -> Document<'a> {
|
||||
// Fn and args
|
||||
let head = "fn".to_doc().append(wrap_args(
|
||||
function.arguments.iter().map(|e| (self.fn_arg(e), false)),
|
||||
));
|
||||
|
||||
// Add return annotation
|
||||
let head = match &function.return_annotation {
|
||||
Some(anno) => head.append(" -> ").append(self.annotation(anno)),
|
||||
None => head,
|
||||
}
|
||||
.group();
|
||||
|
||||
// Format body
|
||||
let body = self.expr(&function.body);
|
||||
|
||||
// Add any trailing comments
|
||||
let body = match printed_comments(self.pop_comments(function.end_position), false) {
|
||||
Some(comments) => body.append(line()).append(comments),
|
||||
None => body,
|
||||
};
|
||||
|
||||
// validator name(params)
|
||||
let v_head = "validator"
|
||||
.to_doc()
|
||||
.append(" ")
|
||||
.append(function.name.as_str())
|
||||
.append(wrap_args(params.iter().map(|e| (self.fn_arg(e), false))));
|
||||
|
||||
// Stick it all together
|
||||
let inner_fn = head
|
||||
.append(" {")
|
||||
.append(line().append(body).nest(INDENT).group())
|
||||
.append(line())
|
||||
.append("}");
|
||||
|
||||
let inner_fn = match printed_comments(self.pop_comments(end_position), false) {
|
||||
Some(comments) => inner_fn.append(line()).append(comments),
|
||||
None => inner_fn,
|
||||
};
|
||||
|
||||
v_head
|
||||
.append(" {")
|
||||
.append(line().append(inner_fn).nest(INDENT).group())
|
||||
.append(line())
|
||||
.append("}")
|
||||
}
|
||||
|
||||
fn expr_fn<'a>(
|
||||
&mut self,
|
||||
args: &'a [UntypedArg],
|
||||
|
|
|
@ -83,6 +83,7 @@ fn module_parser() -> impl Parser<Token, Vec<UntypedDefinition>, Error = ParseEr
|
|||
import_parser(),
|
||||
data_parser(),
|
||||
type_alias_parser(),
|
||||
validator_parser(),
|
||||
fn_parser(),
|
||||
test_parser(),
|
||||
constant_parser(),
|
||||
|
@ -232,6 +233,73 @@ pub fn type_alias_parser() -> impl Parser<Token, ast::UntypedDefinition, Error =
|
|||
})
|
||||
}
|
||||
|
||||
pub fn validator_parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError> {
|
||||
just(Token::Validator)
|
||||
.ignore_then(select! {Token::Name {name} => name})
|
||||
.then(
|
||||
fn_param_parser()
|
||||
.separated_by(just(Token::Comma))
|
||||
.allow_trailing()
|
||||
.delimited_by(just(Token::LeftParen), just(Token::RightParen))
|
||||
.or_not()
|
||||
.map_with_span(|arguments, span| (arguments.unwrap_or_default(), span)),
|
||||
)
|
||||
.then(
|
||||
just(Token::Fn)
|
||||
.ignore_then(
|
||||
fn_param_parser()
|
||||
.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(type_parser()).or_not())
|
||||
.then(
|
||||
expr_seq_parser()
|
||||
.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(expr::UntypedExpr::Todo {
|
||||
kind: TodoKind::EmptyFunction,
|
||||
location: span,
|
||||
label: None,
|
||||
}),
|
||||
doc: None,
|
||||
location: Span {
|
||||
start: span.start,
|
||||
end: return_annotation
|
||||
.as_ref()
|
||||
.map(|l| l.location().end)
|
||||
.unwrap_or_else(|| args_span.end),
|
||||
},
|
||||
end_position: span.end - 1,
|
||||
name: "".to_string(),
|
||||
public: false,
|
||||
return_annotation,
|
||||
return_type: (),
|
||||
},
|
||||
)
|
||||
.delimited_by(just(Token::LeftBrace), just(Token::RightBrace)),
|
||||
)
|
||||
.map_with_span(|((name, (params, params_span)), mut function), span| {
|
||||
function.name = name;
|
||||
|
||||
ast::UntypedDefinition::Validator(ast::Validator {
|
||||
doc: None,
|
||||
function,
|
||||
location: Span {
|
||||
start: span.start,
|
||||
end: params_span.end,
|
||||
},
|
||||
params,
|
||||
end_position: span.end - 1,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn fn_parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError> {
|
||||
pub_parser()
|
||||
.or_not()
|
||||
|
|
|
@ -76,6 +76,7 @@ pub enum Token {
|
|||
Type,
|
||||
When,
|
||||
Trace,
|
||||
Validator,
|
||||
}
|
||||
|
||||
impl fmt::Display for Token {
|
||||
|
@ -156,6 +157,7 @@ impl fmt::Display for Token {
|
|||
Token::Type => "type",
|
||||
Token::Test => "test",
|
||||
Token::ErrorTerm => "error",
|
||||
Token::Validator => "validator",
|
||||
};
|
||||
write!(f, "\"{s}\"")
|
||||
}
|
||||
|
|
|
@ -38,6 +38,28 @@ fn windows_newline() {
|
|||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validator() {
|
||||
let code = indoc! {r#"
|
||||
validator foo {
|
||||
fn(datum, rdmr, ctx) {
|
||||
True
|
||||
}
|
||||
}
|
||||
"#};
|
||||
|
||||
assert_definitions(
|
||||
code,
|
||||
vec![ast::UntypedDefinition::Use(Use {
|
||||
location: Span::new((), 0..12),
|
||||
module: vec!["std".to_string(), "list".to_string()],
|
||||
as_name: None,
|
||||
unqualified: vec![],
|
||||
package: (),
|
||||
})],
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn import() {
|
||||
let code = indoc! {r#"
|
||||
|
|
|
@ -884,6 +884,7 @@ impl<'a> Environment<'a> {
|
|||
})
|
||||
}
|
||||
Definition::Fn { .. }
|
||||
| Definition::Validator { .. }
|
||||
| Definition::Use { .. }
|
||||
| Definition::ModuleConstant { .. }
|
||||
| Definition::Test { .. } => None,
|
||||
|
@ -995,6 +996,7 @@ impl<'a> Environment<'a> {
|
|||
}
|
||||
|
||||
Definition::Fn { .. }
|
||||
| Definition::Validator { .. }
|
||||
| Definition::Test { .. }
|
||||
| Definition::Use { .. }
|
||||
| Definition::ModuleConstant { .. } => {}
|
||||
|
|
|
@ -531,6 +531,7 @@ fn str_to_keyword(word: &str) -> Option<Token> {
|
|||
"trace" => Some(Token::Trace),
|
||||
"test" => Some(Token::Test),
|
||||
"error" => Some(Token::ErrorTerm),
|
||||
"validator" => Some(Token::Validator),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue