abyss_core/parser/
helpers.rs1use std::sync::Arc;
2
3use chumsky::{error::Rich, extra, prelude::*, span::SimpleSpan as ChumskySpan, text};
4
5use crate::ast::LineInfo;
6
7use super::SimpleSpan;
8
9#[derive(Debug, Clone)]
11pub struct LineMap {
12 line_starts: Arc<Vec<usize>>,
13}
14
15impl LineMap {
16 pub fn new(source: &str) -> Self {
17 let mut starts = Vec::with_capacity(source.lines().count() + 1);
18 starts.push(0);
19 for (idx, ch) in source.char_indices() {
20 if ch == '\n' {
21 starts.push(idx + ch.len_utf8());
22 }
23 }
24 LineMap {
25 line_starts: Arc::new(starts),
26 }
27 }
28
29 pub fn line_col(&self, offset: usize) -> (usize, usize) {
30 let line_idx = match self.line_starts.binary_search(&offset) {
31 Ok(idx) => idx,
32 Err(idx) => idx.saturating_sub(1),
33 };
34 let line_start = self.line_starts[line_idx];
35 (line_idx + 1, offset - line_start + 1)
36 }
37
38 pub fn line_info(&self, span: SimpleSpan<usize>) -> Option<LineInfo> {
39 let (line, column) = self.line_col(span.start());
40 Some(LineInfo::new(line, column))
41 }
42}
43
44type LexerExtra<'src> = extra::Err<Rich<'src, char, ChumskySpan<usize>>>;
45
46pub fn abyss_whitespace<'src>() -> impl Parser<'src, &'src str, (), LexerExtra<'src>> + Clone {
48 text::whitespace::<_, LexerExtra<'src>>().to(())
49}
50
51pub fn attach_line_info(map: &LineMap, span: SimpleSpan<usize>) -> Option<LineInfo> {
53 map.line_info(span)
54}
55
56pub fn scrub_comments_preserve_layout(source: &str) -> String {
58 let mut result = String::with_capacity(source.len());
59 let mut chars = source.chars().peekable();
60
61 while let Some(ch) = chars.next() {
62 if ch == '/'
63 && let Some(&next) = chars.peek()
64 {
65 if next == '/' {
66 result.push(' '); chars.next(); result.push(' ');
70
71 while let Some(&c) = chars.peek() {
72 chars.next();
73 if c == '\n' {
74 result.push('\n');
75 break;
76 }
77 result.push(' ');
78 }
79
80 continue;
81 } else if next == '*' {
82 result.push(' '); chars.next(); result.push(' ');
86
87 let mut prev = '\0';
88 for c in chars.by_ref() {
89 if c == '\n' {
90 result.push('\n');
91 } else {
92 result.push(' ');
93 }
94
95 if prev == '*' && c == '/' {
96 break;
97 }
98
99 prev = c;
100 }
101
102 continue;
103 }
104 }
105
106 result.push(ch);
107 }
108
109 result
110}