arity_parser/parser/
diagnostics.rs1use crate::parser::lexer::{TokKind, Token};
2
3#[derive(Debug, Clone, PartialEq, Eq)]
4pub struct ParseDiagnostic {
5 pub message: String,
6 pub start: usize,
7 pub end: usize,
8}
9
10pub(crate) const MISSING_STATEMENT_SEPARATOR: &str = "expected a newline or ';' between statements";
15
16pub(crate) fn push_diagnostic(
17 diagnostics: &mut Vec<ParseDiagnostic>,
18 message: &str,
19 start: usize,
20 end: usize,
21) {
22 diagnostics.push(ParseDiagnostic {
23 message: message.to_string(),
24 start,
25 end,
26 });
27}
28
29pub(crate) fn push_token_diagnostic(
30 diagnostics: &mut Vec<ParseDiagnostic>,
31 message: &str,
32 token: &Token,
33) {
34 push_diagnostic(diagnostics, message, token.start, token.end);
35}
36
37pub(crate) fn refine_unicode_diagnostics(tokens: &[Token], diagnostics: &mut Vec<ParseDiagnostic>) {
41 let specific: Vec<_> = tokens
42 .iter()
43 .filter(|token| token.kind == TokKind::Unknown)
44 .filter_map(|token| {
45 let ch = token.text.chars().next()?;
46 let (description, advice) = match ch {
47 '\u{200b}'..='\u{200d}' | '\u{180e}' | '\u{feff}' => {
48 ("zero-width character", "remove this character")
49 }
50 '\u{ad}' => ("invisible soft hyphen", "remove this character"),
51 '\u{34f}' => (
52 "invisible combining grapheme joiner",
53 "remove this character",
54 ),
55 '\u{61c}'
56 | '\u{200e}'..='\u{200f}'
57 | '\u{202a}'..='\u{202e}'
58 | '\u{2066}'..='\u{2069}' => {
59 ("invisible direction control", "remove this character")
60 }
61 '\u{2060}' => ("invisible word joiner", "remove this character"),
62 '\u{2061}'..='\u{2064}' => ("invisible operator", "use an explicit R operator"),
63 '\u{a0}' | '\u{2007}' | '\u{202f}' => ("Unicode space", "use an ASCII space"),
64 '\u{85}' => ("Unicode next-line character", "use a newline"),
65 '\u{2028}' => ("Unicode line separator", "use a newline"),
66 '\u{2029}' => ("Unicode paragraph separator", "use a newline"),
67 '\u{2212}' => ("Unicode minus sign", "use ASCII '-'"),
68 _ => return None,
69 };
70 Some(ParseDiagnostic {
71 message: format!(
72 "unexpected {description} U+{:04X} in R code; {advice}",
73 ch as u32
74 ),
75 start: token.start,
76 end: token.end,
77 })
78 })
79 .collect();
80 if specific.is_empty() {
81 return;
82 }
83 diagnostics.retain(|d| {
87 specific
88 .binary_search_by_key(&(d.start, d.end), |s| (s.start, s.end))
89 .is_err()
90 });
91 diagnostics.extend(specific);
92 diagnostics.sort_by_key(|d| (d.start, d.end));
93}