feat(definitions):

* add parsing for new validator defs
* start adding typechecking
* add a unit test for parsing
This commit is contained in:
rvcas 2023-02-14 00:24:40 -05:00 committed by Lucas
parent dfe240ad64
commit 2e7fe191db
7 changed files with 172 additions and 1 deletions

View File

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

View File

@ -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],

View File

@ -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()

View File

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

View File

@ -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#"

View File

@ -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 { .. } => {}

View File

@ -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,
}
}