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 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 TypedDefinition = Definition<Arc<Type>, TypedExpr, String, String>;
|
||||||
pub type UntypedDefinition = Definition<(), UntypedExpr, (), ()>;
|
pub type UntypedDefinition = Definition<(), UntypedExpr, (), ()>;
|
||||||
|
|
||||||
|
@ -274,6 +286,8 @@ pub enum Definition<T, Expr, ConstantRecordTag, PackageName> {
|
||||||
ModuleConstant(ModuleConstant<T, ConstantRecordTag>),
|
ModuleConstant(ModuleConstant<T, ConstantRecordTag>),
|
||||||
|
|
||||||
Test(Function<T, Expr>),
|
Test(Function<T, Expr>),
|
||||||
|
|
||||||
|
Validator(Validator<T, Expr>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A, B, C, E> Definition<A, B, C, E> {
|
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::TypeAlias(TypeAlias { location, .. })
|
||||||
| Definition::DataType(DataType { location, .. })
|
| Definition::DataType(DataType { location, .. })
|
||||||
| Definition::ModuleConstant(ModuleConstant { location, .. })
|
| Definition::ModuleConstant(ModuleConstant { location, .. })
|
||||||
|
| Definition::Validator(Validator { location, .. })
|
||||||
| Definition::Test(Function { location, .. }) => *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::TypeAlias(TypeAlias { doc, .. })
|
||||||
| Definition::DataType(DataType { doc, .. })
|
| Definition::DataType(DataType { doc, .. })
|
||||||
| Definition::ModuleConstant(ModuleConstant { doc, .. })
|
| Definition::ModuleConstant(ModuleConstant { doc, .. })
|
||||||
|
| Definition::Validator(Validator { doc, .. })
|
||||||
| Definition::Test(Function { doc, .. }) => {
|
| Definition::Test(Function { doc, .. }) => {
|
||||||
let _ = std::mem::replace(doc, Some(new_doc));
|
let _ = std::mem::replace(doc, Some(new_doc));
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ use crate::{
|
||||||
Definition, Function, IfBranch, ModuleConstant, Pattern, RecordConstructor,
|
Definition, Function, IfBranch, ModuleConstant, Pattern, RecordConstructor,
|
||||||
RecordConstructorArg, RecordUpdateSpread, Span, TraceKind, TypeAlias, TypedArg,
|
RecordConstructorArg, RecordUpdateSpread, Span, TraceKind, TypeAlias, TypedArg,
|
||||||
TypedConstant, UnOp, UnqualifiedImport, UntypedArg, UntypedClause, UntypedClauseGuard,
|
TypedConstant, UnOp, UnqualifiedImport, UntypedArg, UntypedClause, UntypedClauseGuard,
|
||||||
UntypedDefinition, UntypedModule, UntypedPattern, UntypedRecordUpdateArg, Use,
|
UntypedDefinition, UntypedModule, UntypedPattern, UntypedRecordUpdateArg, Use, Validator,
|
||||||
CAPTURE_VARIABLE,
|
CAPTURE_VARIABLE,
|
||||||
},
|
},
|
||||||
docvec,
|
docvec,
|
||||||
|
@ -236,6 +236,13 @@ impl<'comments> Formatter<'comments> {
|
||||||
*end_position,
|
*end_position,
|
||||||
),
|
),
|
||||||
|
|
||||||
|
Definition::Validator(Validator {
|
||||||
|
end_position,
|
||||||
|
function,
|
||||||
|
params,
|
||||||
|
..
|
||||||
|
}) => self.definition_validator(params, function, *end_position),
|
||||||
|
|
||||||
Definition::Test(Function {
|
Definition::Test(Function {
|
||||||
name,
|
name,
|
||||||
arguments: args,
|
arguments: args,
|
||||||
|
@ -550,6 +557,59 @@ impl<'comments> Formatter<'comments> {
|
||||||
.append("}")
|
.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>(
|
fn expr_fn<'a>(
|
||||||
&mut self,
|
&mut self,
|
||||||
args: &'a [UntypedArg],
|
args: &'a [UntypedArg],
|
||||||
|
|
|
@ -83,6 +83,7 @@ fn module_parser() -> impl Parser<Token, Vec<UntypedDefinition>, Error = ParseEr
|
||||||
import_parser(),
|
import_parser(),
|
||||||
data_parser(),
|
data_parser(),
|
||||||
type_alias_parser(),
|
type_alias_parser(),
|
||||||
|
validator_parser(),
|
||||||
fn_parser(),
|
fn_parser(),
|
||||||
test_parser(),
|
test_parser(),
|
||||||
constant_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 fn fn_parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError> {
|
||||||
pub_parser()
|
pub_parser()
|
||||||
.or_not()
|
.or_not()
|
||||||
|
|
|
@ -76,6 +76,7 @@ pub enum Token {
|
||||||
Type,
|
Type,
|
||||||
When,
|
When,
|
||||||
Trace,
|
Trace,
|
||||||
|
Validator,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Token {
|
impl fmt::Display for Token {
|
||||||
|
@ -156,6 +157,7 @@ impl fmt::Display for Token {
|
||||||
Token::Type => "type",
|
Token::Type => "type",
|
||||||
Token::Test => "test",
|
Token::Test => "test",
|
||||||
Token::ErrorTerm => "error",
|
Token::ErrorTerm => "error",
|
||||||
|
Token::Validator => "validator",
|
||||||
};
|
};
|
||||||
write!(f, "\"{s}\"")
|
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]
|
#[test]
|
||||||
fn import() {
|
fn import() {
|
||||||
let code = indoc! {r#"
|
let code = indoc! {r#"
|
||||||
|
|
|
@ -884,6 +884,7 @@ impl<'a> Environment<'a> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Definition::Fn { .. }
|
Definition::Fn { .. }
|
||||||
|
| Definition::Validator { .. }
|
||||||
| Definition::Use { .. }
|
| Definition::Use { .. }
|
||||||
| Definition::ModuleConstant { .. }
|
| Definition::ModuleConstant { .. }
|
||||||
| Definition::Test { .. } => None,
|
| Definition::Test { .. } => None,
|
||||||
|
@ -995,6 +996,7 @@ impl<'a> Environment<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Definition::Fn { .. }
|
Definition::Fn { .. }
|
||||||
|
| Definition::Validator { .. }
|
||||||
| Definition::Test { .. }
|
| Definition::Test { .. }
|
||||||
| Definition::Use { .. }
|
| Definition::Use { .. }
|
||||||
| Definition::ModuleConstant { .. } => {}
|
| Definition::ModuleConstant { .. } => {}
|
||||||
|
|
|
@ -531,6 +531,7 @@ fn str_to_keyword(word: &str) -> Option<Token> {
|
||||||
"trace" => Some(Token::Trace),
|
"trace" => Some(Token::Trace),
|
||||||
"test" => Some(Token::Test),
|
"test" => Some(Token::Test),
|
||||||
"error" => Some(Token::ErrorTerm),
|
"error" => Some(Token::ErrorTerm),
|
||||||
|
"validator" => Some(Token::Validator),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue