1use rowan::{SyntaxElement, SyntaxToken, TextRange};
27use smol_str::SmolStr;
28
29use crate::ast::{
30 Arg, AssignmentExpr, AstNode, BlockExpr, CallExpr, HasArgList, IfExpr, Root, token_name,
31};
32use crate::parser;
33use crate::syntax::{RLanguage, SyntaxKind, SyntaxNode};
34
35pub use crate::parser::ParseDiagnostic;
36
37#[derive(Debug, Clone)]
39pub struct ParseOutput {
40 pub cst: SyntaxNode,
42 pub diagnostics: Vec<ParseDiagnostic>,
44}
45
46impl ParseOutput {
47 pub fn document(&self) -> Document {
49 Document {
50 root: Root::cast(self.cst.clone()).expect("the R parser always roots the tree at ROOT"),
51 }
52 }
53}
54
55pub fn parse(text: &str) -> ParseOutput {
62 let parsed = parser::parse(text);
63 let document = Document {
64 root: Root::cast(parsed.cst.clone()).expect("the R parser always roots the tree at ROOT"),
65 };
66 let mut diagnostics = parsed.diagnostics;
67
68 for entry in document.entries() {
69 match entry {
70 Entry::Directive(directive) if directive.kind() == DirectiveKind::Unsupported => {
71 let Some(name) = directive.name_token() else {
72 continue;
73 };
74 push_if_uncovered(
75 &mut diagnostics,
76 "unsupported NAMESPACE directive",
77 name.text_range(),
78 );
79 }
80 Entry::Malformed(malformed) => push_if_uncovered(
81 &mut diagnostics,
82 "expected a NAMESPACE directive",
83 malformed.text_range(),
84 ),
85 Entry::Directive(_) => {}
86 }
87 }
88
89 diagnostics.sort_by_key(|diagnostic| (diagnostic.start, diagnostic.end));
90 ParseOutput {
91 cst: parsed.cst,
92 diagnostics,
93 }
94}
95
96pub fn reconstruct(text: &str) -> String {
98 parse(text)
99 .cst
100 .descendants_with_tokens()
101 .filter_map(|element| element.into_token())
102 .map(|token| token.text().to_string())
103 .collect()
104}
105
106fn push_if_uncovered(diagnostics: &mut Vec<ParseDiagnostic>, message: &str, range: TextRange) {
107 let start = usize::from(range.start());
108 let end = usize::from(range.end());
109 let overlaps_existing = diagnostics
110 .iter()
111 .any(|diagnostic| diagnostic.start < end && start < diagnostic.end);
112 if !overlaps_existing {
113 diagnostics.push(ParseDiagnostic {
114 message: message.to_string(),
115 start,
116 end,
117 });
118 }
119}
120
121#[derive(Debug, Clone, PartialEq, Eq, Hash)]
123pub struct Document {
124 root: Root,
125}
126
127impl Document {
128 pub fn syntax(&self) -> &SyntaxNode {
130 self.root.syntax()
131 }
132
133 pub fn entries(&self) -> impl Iterator<Item = Entry> + use<> {
139 let mut entries = Vec::new();
140 for element in self.root.syntax().children_with_tokens() {
141 collect_entry(element, None, &mut entries);
142 }
143 entries.into_iter()
144 }
145
146 pub fn directives(&self) -> impl Iterator<Item = Directive> + use<> {
148 self.entries().filter_map(|entry| match entry {
149 Entry::Directive(directive) => Some(directive),
150 Entry::Malformed(_) => None,
151 })
152 }
153}
154
155#[derive(Debug, Clone, PartialEq, Eq, Hash)]
157pub enum Entry {
158 Directive(Directive),
160 Malformed(Malformed),
162}
163
164#[derive(Debug, Clone, PartialEq, Eq, Hash)]
166pub struct Directive {
167 call: CallExpr,
168 range: TextRange,
169}
170
171impl Directive {
172 pub fn kind(&self) -> DirectiveKind {
174 self.name()
175 .as_deref()
176 .map(DirectiveKind::from_name)
177 .unwrap_or(DirectiveKind::Unsupported)
178 }
179
180 pub fn name(&self) -> Option<SmolStr> {
182 self.name_token().as_ref().map(token_name)
183 }
184
185 pub fn name_token(&self) -> Option<SyntaxToken<RLanguage>> {
187 direct_callee_token(&self.call)
188 }
189
190 pub fn syntax(&self) -> &SyntaxNode {
192 self.call.syntax()
193 }
194
195 pub fn text_range(&self) -> TextRange {
200 self.range
201 }
202
203 pub fn call_range(&self) -> TextRange {
205 self.call.syntax().text_range()
206 }
207
208 pub fn l_paren(&self) -> Option<SyntaxToken<RLanguage>> {
210 child_token(self.call.syntax(), SyntaxKind::LPAREN)
211 }
212
213 pub fn r_paren(&self) -> Option<SyntaxToken<RLanguage>> {
215 child_token(self.call.syntax(), SyntaxKind::RPAREN)
216 }
217
218 pub fn arguments(&self) -> impl Iterator<Item = Argument> + '_ {
220 self.call.args().map(|argument| Argument { argument })
221 }
222}
223
224#[derive(Debug, Clone, PartialEq, Eq, Hash)]
226pub struct Argument {
227 argument: Arg,
228}
229
230impl Argument {
231 pub fn syntax(&self) -> &SyntaxNode {
233 self.argument.syntax()
234 }
235
236 pub fn text_range(&self) -> TextRange {
238 self.argument.syntax().text_range()
239 }
240
241 pub fn name(&self) -> Option<SmolStr> {
243 self.argument.name()
244 }
245
246 pub fn name_token(&self) -> Option<SyntaxToken<RLanguage>> {
248 self.argument.name_token()
249 }
250
251 pub fn value(&self) -> Option<SyntaxElement<RLanguage>> {
253 self.argument.value()
254 }
255
256 pub fn value_range(&self) -> Option<TextRange> {
258 self.value().map(|value| value.text_range())
259 }
260}
261
262#[derive(Debug, Clone, PartialEq, Eq, Hash)]
265pub struct Malformed {
266 syntax: SyntaxElement<RLanguage>,
267 range: TextRange,
268}
269
270impl Malformed {
271 pub fn syntax(&self) -> &SyntaxElement<RLanguage> {
273 &self.syntax
274 }
275
276 pub fn text_range(&self) -> TextRange {
278 self.range
279 }
280}
281
282#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
284pub enum DirectiveKind {
285 Export,
286 ExportPattern,
287 ExportClassPattern,
288 ExportClass,
289 ExportClasses,
290 ExportMethods,
291 Import,
292 ImportFrom,
293 ImportClassFrom,
294 ImportClassesFrom,
295 ImportMethodsFrom,
296 UseDynLib,
297 S3Method,
298 Unsupported,
300}
301
302impl DirectiveKind {
303 pub fn from_name(name: &str) -> Self {
305 match name {
306 "export" => Self::Export,
307 "exportPattern" => Self::ExportPattern,
308 "exportClassPattern" => Self::ExportClassPattern,
309 "exportClass" => Self::ExportClass,
310 "exportClasses" => Self::ExportClasses,
311 "exportMethods" => Self::ExportMethods,
312 "import" => Self::Import,
313 "importFrom" => Self::ImportFrom,
314 "importClassFrom" => Self::ImportClassFrom,
315 "importClassesFrom" => Self::ImportClassesFrom,
316 "importMethodsFrom" => Self::ImportMethodsFrom,
317 "useDynLib" => Self::UseDynLib,
318 "S3method" => Self::S3Method,
319 _ => Self::Unsupported,
320 }
321 }
322}
323
324fn child_token(node: &SyntaxNode, kind: SyntaxKind) -> Option<SyntaxToken<RLanguage>> {
325 node.children_with_tokens()
326 .find_map(|element| match element {
327 SyntaxElement::Token(token) if token.kind() == kind => Some(token),
328 _ => None,
329 })
330}
331
332fn direct_callee_token(call: &CallExpr) -> Option<SyntaxToken<RLanguage>> {
333 match call.base()? {
334 SyntaxElement::Token(token)
335 if matches!(token.kind(), SyntaxKind::IDENT | SyntaxKind::STRING) =>
336 {
337 Some(token)
338 }
339 _ => None,
340 }
341}
342
343fn collect_entry(
344 element: SyntaxElement<RLanguage>,
345 enclosing_range: Option<TextRange>,
346 entries: &mut Vec<Entry>,
347) {
348 if is_namespace_trivia(&element) {
349 return;
350 }
351
352 let range = enclosing_range.unwrap_or_else(|| element.text_range());
353 let SyntaxElement::Node(node) = element.clone() else {
354 entries.push(Entry::Malformed(Malformed {
355 syntax: element,
356 range,
357 }));
358 return;
359 };
360
361 match node.kind() {
362 SyntaxKind::CALL_EXPR => {
363 let call = CallExpr::cast(node).expect("CALL_EXPR always casts");
364 if direct_callee_token(&call).is_some() {
365 entries.push(Entry::Directive(Directive { call, range }));
366 } else {
367 entries.push(Entry::Malformed(Malformed {
368 syntax: element,
369 range,
370 }));
371 }
372 }
373 SyntaxKind::BLOCK_EXPR => {
374 let block = BlockExpr::cast(node).expect("BLOCK_EXPR always casts");
375 for statement in block.statements() {
376 collect_entry(statement, enclosing_range, entries);
377 }
378 }
379 SyntaxKind::IF_EXPR => {
380 let conditional = IfExpr::cast(node).expect("IF_EXPR always casts");
381 if let Some(then_elements) = conditional.then_elements() {
382 for branch_element in then_elements {
383 collect_entry(branch_element, enclosing_range, entries);
384 }
385 }
386 if let Some(else_elements) = conditional.else_elements() {
387 for branch_element in else_elements {
388 collect_entry(branch_element, enclosing_range, entries);
389 }
390 }
391 }
392 SyntaxKind::ASSIGNMENT_EXPR => {
393 let assignment = AssignmentExpr::cast(node).expect("ASSIGNMENT_EXPR always casts");
394 if matches!(
395 assignment.op_kind(),
396 Some(SyntaxKind::ASSIGN_EQ | SyntaxKind::ASSIGN_LEFT)
397 ) && let Some(value) = assignment.value_element()
398 {
399 collect_entry(value, Some(range), entries);
400 } else {
401 entries.push(Entry::Malformed(Malformed {
402 syntax: element,
403 range,
404 }));
405 }
406 }
407 SyntaxKind::ROXYGEN_BLOCK => {}
411 _ => entries.push(Entry::Malformed(Malformed {
412 syntax: element,
413 range,
414 })),
415 }
416}
417
418fn is_namespace_trivia(element: &SyntaxElement<RLanguage>) -> bool {
419 matches!(
420 element.kind(),
421 SyntaxKind::WHITESPACE | SyntaxKind::NEWLINE | SyntaxKind::SEMICOLON | SyntaxKind::COMMENT
422 )
423}