arity_parser/parser/validate.rs
1//! Judging a parse against R's own acceptance rules.
2//!
3//! arity's parser is deliberately more lenient than R's: it recovers from
4//! errors, and its lexer accepts name shapes R rejects. Consumers that need to
5//! answer "would R have parsed this?" — rather than "did arity produce a usable
6//! tree?" — go through the predicates here.
7
8use crate::parser::core::ParseOutput;
9use crate::syntax::{SyntaxKind, SyntaxNode};
10
11/// Whether the parsed text is exactly one complete R expression, the way R's
12/// own parser judges it — the predicate behind `rlang::parse_expr()` (and so
13/// roxygen2's `can_parse()`).
14///
15/// This takes the whole [`ParseOutput`] rather than just the CST because a
16/// recovered tree alone cannot answer the question: `f(` yields a well-shaped
17/// `CALL_EXPR` and reports the missing `)` only as a diagnostic.
18///
19/// Three things have to hold:
20///
21/// - the parse is diagnostic-free;
22/// - the root holds one expression, optionally followed by a single `;`. R's
23/// `;` is a statement *terminator*, not a separator that may stand alone: it
24/// must follow an expression on the same line, and a second one opens an
25/// empty statement, which is a syntax error (`x;` parses, `;x`, `x;;`,
26/// `x\n;` and `x; y` do not);
27/// - no name in the tree is one R's lexer rejects but arity's accepts (see
28/// [`has_r_invalid_name`]).
29pub fn is_single_expression(out: &ParseOutput) -> bool {
30 out.diagnostics.is_empty()
31 && root_holds_one_expression(&out.cst)
32 && !has_r_invalid_name(&out.cst)
33}
34
35/// The positional `expr` / `expr ;` rule over the root's children.
36fn root_holds_one_expression(cst: &SyntaxNode) -> bool {
37 let mut expr = false;
38 let mut semicolon = false;
39 let mut line_broke = false;
40 for el in cst.children_with_tokens() {
41 match el.kind() {
42 SyntaxKind::WHITESPACE | SyntaxKind::COMMENT => {}
43 SyntaxKind::NEWLINE => line_broke = true,
44 SyntaxKind::SEMICOLON => {
45 if !expr || semicolon || line_broke {
46 return false;
47 }
48 semicolon = true;
49 }
50 _ => {
51 if expr {
52 return false;
53 }
54 expr = true;
55 line_broke = false;
56 }
57 }
58 }
59 expr
60}
61
62/// Whether the tree contains a name R's lexer rejects but arity's more lenient
63/// one accepts as an ordinary identifier.
64///
65/// A `_`-leading name errors in R's lexer, with one exception: a lone `_` used
66/// as the native-pipe placeholder, valid only inside a `|>` pipeline (a
67/// `_`-leading name of length ≥ 2 is never valid). A zero-length backquoted
68/// name `` `` `` errors at parse time ("attempt to use zero-length variable
69/// name"); any non-empty backquoted name is valid.
70pub fn has_r_invalid_name(cst: &SyntaxNode) -> bool {
71 let has_pipe = cst
72 .descendants_with_tokens()
73 .any(|el| el.kind() == SyntaxKind::PIPE);
74 cst.descendants_with_tokens()
75 .filter_map(|el| el.into_token())
76 .filter(|t| t.kind() == SyntaxKind::IDENT)
77 .any(|t| {
78 let text = t.text();
79 text == "``" || (text.starts_with('_') && (text.len() > 1 || !has_pipe))
80 })
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86 use crate::parser::core::parse;
87
88 fn single(text: &str) -> bool {
89 is_single_expression(&parse(text))
90 }
91
92 #[test]
93 fn accepts_one_expression() {
94 assert!(single("x"));
95 assert!(single("f(a, b)"));
96 assert!(single("function(x) x + 1"));
97 assert!(single(" x "));
98 assert!(single("\nx\n"));
99 assert!(single("x # trailing comment"));
100 }
101
102 #[test]
103 fn rejects_zero_or_several_expressions() {
104 assert!(!single(""));
105 assert!(!single(" "));
106 assert!(!single("# just a comment"));
107 assert!(!single("inline code"));
108 assert!(!single("x\ny"));
109 }
110
111 #[test]
112 fn rejects_an_incomplete_expression() {
113 assert!(!single("f("));
114 assert!(!single("x +"));
115 }
116
117 #[test]
118 fn semicolon_terminates_but_never_stands_alone() {
119 // Cross-checked against `rlang::parse_expr()` under R 4.6.1.
120 assert!(single("x;"));
121 assert!(single("x ;"));
122 assert!(single("f(a);"));
123 assert!(single("x;\n"));
124 assert!(single("x; # trailing comment"));
125 assert!(!single(";"));
126 assert!(!single(";x"));
127 assert!(!single("x;;"));
128 assert!(!single("x; ;"));
129 assert!(!single("x; y"));
130 assert!(!single("x;\ny"));
131 // A `;` on its own line opens an empty statement, so it is an error
132 // even though the expression before it is complete.
133 assert!(!single("x\n;"));
134 }
135
136 #[test]
137 fn rejects_names_r_lexes_as_errors() {
138 assert!(!single("_"));
139 assert!(!single("_x"));
140 assert!(!single("``"));
141 assert!(single("x |> _$col"));
142 assert!(single("a_b"));
143 assert!(single("`x`"));
144 }
145}