Implement APIs for parsing expressions, types, paths, patterns and items

This commit is contained in:
David Lattimore 2020-06-19 07:43:19 +10:00
parent 902a9c6da7
commit bc99e95d7d
29 changed files with 222 additions and 1 deletions

View File

@ -168,6 +168,41 @@ impl SourceFile {
} }
} }
impl ast::Path {
/// Returns `text`, parsed as a path, but only if it has no errors.
pub fn parse(text: &str) -> Result<Self, ()> {
parsing::parse_text_fragment(text, ra_parser::FragmentKind::Path)
}
}
impl ast::Pat {
/// Returns `text`, parsed as a pattern, but only if it has no errors.
pub fn parse(text: &str) -> Result<Self, ()> {
parsing::parse_text_fragment(text, ra_parser::FragmentKind::Pattern)
}
}
impl ast::Expr {
/// Returns `text`, parsed as an expression, but only if it has no errors.
pub fn parse(text: &str) -> Result<Self, ()> {
parsing::parse_text_fragment(text, ra_parser::FragmentKind::Expr)
}
}
impl ast::ModuleItem {
/// Returns `text`, parsed as an item, but only if it has no errors.
pub fn parse(text: &str) -> Result<Self, ()> {
parsing::parse_text_fragment(text, ra_parser::FragmentKind::Item)
}
}
impl ast::TypeRef {
/// Returns `text`, parsed as an type reference, but only if it has no errors.
pub fn parse(text: &str) -> Result<Self, ()> {
parsing::parse_text_fragment(text, ra_parser::FragmentKind::Type)
}
}
/// Matches a `SyntaxNode` against an `ast` type. /// Matches a `SyntaxNode` against an `ast` type.
/// ///
/// # Example: /// # Example:

View File

@ -6,13 +6,14 @@ mod text_token_source;
mod text_tree_sink; mod text_tree_sink;
mod reparsing; mod reparsing;
use crate::{syntax_node::GreenNode, SyntaxError}; use crate::{syntax_node::GreenNode, AstNode, SyntaxError, SyntaxNode};
use text_token_source::TextTokenSource; use text_token_source::TextTokenSource;
use text_tree_sink::TextTreeSink; use text_tree_sink::TextTreeSink;
pub use lexer::*; pub use lexer::*;
pub(crate) use self::reparsing::incremental_reparse; pub(crate) use self::reparsing::incremental_reparse;
use ra_parser::SyntaxKind;
pub(crate) fn parse_text(text: &str) -> (GreenNode, Vec<SyntaxError>) { pub(crate) fn parse_text(text: &str) -> (GreenNode, Vec<SyntaxError>) {
let (tokens, lexer_errors) = tokenize(&text); let (tokens, lexer_errors) = tokenize(&text);
@ -27,3 +28,32 @@ pub(crate) fn parse_text(text: &str) -> (GreenNode, Vec<SyntaxError>) {
(tree, parser_errors) (tree, parser_errors)
} }
/// Returns `text` parsed as a `T` provided there are no parse errors.
pub(crate) fn parse_text_fragment<T: AstNode>(
text: &str,
fragment_kind: ra_parser::FragmentKind,
) -> Result<T, ()> {
let (tokens, lexer_errors) = tokenize(&text);
if !lexer_errors.is_empty() {
return Err(());
}
let mut token_source = TextTokenSource::new(text, &tokens);
let mut tree_sink = TextTreeSink::new(text, &tokens);
// TextTreeSink assumes that there's at least some root node to which it can attach errors and
// tokens. We arbitrarily give it a SourceFile.
use ra_parser::TreeSink;
tree_sink.start_node(SyntaxKind::SOURCE_FILE);
ra_parser::parse_fragment(&mut token_source, &mut tree_sink, fragment_kind);
tree_sink.finish_node();
let (tree, parser_errors) = tree_sink.finish();
use ra_parser::TokenSource;
if !parser_errors.is_empty() || token_source.current().kind != SyntaxKind::EOF {
return Err(());
}
SyntaxNode::new_root(tree).first_child().and_then(T::cast).ok_or(())
}

View File

@ -54,6 +54,51 @@ fn parser_tests() {
}); });
} }
#[test]
fn expr_parser_tests() {
fragment_parser_dir_test(
&["parser/fragments/expr/ok"],
&["parser/fragments/expr/err"],
crate::ast::Expr::parse,
);
}
#[test]
fn path_parser_tests() {
fragment_parser_dir_test(
&["parser/fragments/path/ok"],
&["parser/fragments/path/err"],
crate::ast::Path::parse,
);
}
#[test]
fn pattern_parser_tests() {
fragment_parser_dir_test(
&["parser/fragments/pattern/ok"],
&["parser/fragments/pattern/err"],
crate::ast::Pat::parse,
);
}
#[test]
fn item_parser_tests() {
fragment_parser_dir_test(
&["parser/fragments/item/ok"],
&["parser/fragments/item/err"],
crate::ast::ModuleItem::parse,
);
}
#[test]
fn type_parser_tests() {
fragment_parser_dir_test(
&["parser/fragments/type/ok"],
&["parser/fragments/type/err"],
crate::ast::TypeRef::parse,
);
}
#[test] #[test]
fn parser_fuzz_tests() { fn parser_fuzz_tests() {
for (_, text) in collect_rust_files(&test_data_dir(), &["parser/fuzz-failures"]) { for (_, text) in collect_rust_files(&test_data_dir(), &["parser/fuzz-failures"]) {
@ -134,3 +179,24 @@ fn dump_tokens_and_errors(tokens: &[Token], errors: &[SyntaxError], text: &str)
} }
acc acc
} }
fn fragment_parser_dir_test<T, F>(ok_paths: &[&str], err_paths: &[&str], f: F)
where
T: crate::AstNode,
F: Fn(&str) -> Result<T, ()>,
{
dir_tests(&test_data_dir(), ok_paths, "rast", |text, path| {
if let Ok(node) = f(text) {
format!("{:#?}", crate::ast::AstNode::syntax(&node))
} else {
panic!("Failed to parse '{:?}'", path);
}
});
dir_tests(&test_data_dir(), err_paths, "rast", |text, path| {
if let Ok(_) = f(text) {
panic!("'{:?}' successfully parsed when it should have errored", path);
} else {
"ERROR\n".to_owned()
}
});
}

View File

@ -0,0 +1,8 @@
BIN_EXPR@0..5
LITERAL@0..1
INT_NUMBER@0..1 "1"
WHITESPACE@1..2 " "
PLUS@2..3 "+"
WHITESPACE@3..4 " "
LITERAL@4..5
INT_NUMBER@4..5 "2"

View File

@ -0,0 +1 @@
1 + 2

View File

@ -0,0 +1 @@
fn fn foo() {}

View File

@ -0,0 +1,12 @@
FN_DEF@0..11
FN_KW@0..2 "fn"
WHITESPACE@2..3 " "
NAME@3..6
IDENT@3..6 "foo"
PARAM_LIST@6..8
L_PAREN@6..7 "("
R_PAREN@7..8 ")"
WHITESPACE@8..9 " "
BLOCK_EXPR@9..11
L_CURLY@9..10 "{"
R_CURLY@10..11 "}"

View File

@ -0,0 +1 @@
fn foo() {}

View File

@ -0,0 +1,4 @@
PATH@0..3
PATH_SEGMENT@0..3
NAME_REF@0..3
IDENT@0..3 "foo"

View File

@ -0,0 +1,14 @@
PATH@0..13
PATH@0..8
PATH@0..3
PATH_SEGMENT@0..3
NAME_REF@0..3
IDENT@0..3 "foo"
COLON2@3..5 "::"
PATH_SEGMENT@5..8
NAME_REF@5..8
IDENT@5..8 "bar"
COLON2@8..10 "::"
PATH_SEGMENT@10..13
NAME_REF@10..13
IDENT@10..13 "baz"

View File

@ -0,0 +1 @@
foo::bar::baz

View File

@ -0,0 +1,10 @@
TUPLE_STRUCT_PAT@0..7
PATH@0..4
PATH_SEGMENT@0..4
NAME_REF@0..4
IDENT@0..4 "Some"
L_PAREN@4..5 "("
BIND_PAT@5..6
NAME@5..6
IDENT@5..6 "x"
R_PAREN@6..7 ")"

View File

@ -0,0 +1 @@
Some(x)

View File

@ -0,0 +1 @@
Result<Foo, Bar

View File

@ -0,0 +1,22 @@
PATH_TYPE@0..16
PATH@0..16
PATH_SEGMENT@0..16
NAME_REF@0..6
IDENT@0..6 "Result"
TYPE_ARG_LIST@6..16
L_ANGLE@6..7 "<"
TYPE_ARG@7..10
PATH_TYPE@7..10
PATH@7..10
PATH_SEGMENT@7..10
NAME_REF@7..10
IDENT@7..10 "Foo"
COMMA@10..11 ","
WHITESPACE@11..12 " "
TYPE_ARG@12..15
PATH_TYPE@12..15
PATH@12..15
PATH_SEGMENT@12..15
NAME_REF@12..15
IDENT@12..15 "Bar"
R_ANGLE@15..16 ">"

View File

@ -0,0 +1 @@
Result<Foo, Bar>