1use std::sync::Arc;
4
5#[derive(Debug, Clone, PartialEq, Eq)]
7pub struct Span {
8 pub line: u32,
9 pub col: u32,
10 pub end_col: u32,
12 pub file: Option<Arc<str>>,
17}
18
19impl Span {
20 pub fn new(line: u32, col: u32, end_col: u32) -> Self {
21 Self {
22 line,
23 col,
24 end_col,
25 file: None,
26 }
27 }
28
29 pub fn in_file(mut self, file: Option<Arc<str>>) -> Self {
30 self.file = file;
31 self
32 }
33}
34
35#[derive(Debug, Clone, PartialEq, Eq)]
37pub struct Diagnostic {
38 pub message: String,
39 pub line: Option<u32>,
40 pub col: Option<u32>,
41 pub end_col: Option<u32>,
42 pub file: Option<Arc<str>>,
45 pub kind: String,
46 pub found: Option<String>,
47 pub expected: Option<String>,
48 pub hint: Option<String>,
49}
50
51impl Diagnostic {
52 pub fn new(kind: impl Into<String>, message: impl Into<String>) -> Self {
53 Self {
54 message: message.into(),
55 line: None,
56 col: None,
57 end_col: None,
58 file: None,
59 kind: kind.into(),
60 found: None,
61 expected: None,
62 hint: None,
63 }
64 }
65
66 pub fn at(mut self, span: Span) -> Self {
67 self.line = Some(span.line);
68 self.col = Some(span.col);
69 self.end_col = Some(span.end_col);
70 self.file = span.file;
71 self
72 }
73
74 pub fn expected(mut self, expected: impl Into<String>) -> Self {
75 self.expected = Some(expected.into());
76 self
77 }
78
79 pub fn found(mut self, found: impl Into<String>) -> Self {
80 self.found = Some(found.into());
81 self
82 }
83
84 pub fn hint(mut self, hint: impl Into<String>) -> Self {
85 self.hint = Some(hint.into());
86 self
87 }
88}
89
90#[derive(Debug, Clone, PartialEq, Eq)]
95pub struct CompileError {
96 pub message: String,
97 pub info: Box<Diagnostic>,
99}
100
101impl std::fmt::Display for CompileError {
102 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103 f.write_str(&self.message)
104 }
105}
106
107impl std::error::Error for CompileError {}
108
109pub(crate) fn closest(word: &str, candidates: &'static [&'static str]) -> Option<&'static str> {
112 candidates
113 .iter()
114 .copied()
115 .filter_map(|candidate| {
116 let dist = edit_distance(word, candidate);
117 (dist <= 2).then_some((dist, candidate))
118 })
119 .min_by_key(|(dist, candidate)| (*dist, *candidate))
120 .map(|(_, candidate)| candidate)
121}
122
123fn edit_distance(a: &str, b: &str) -> usize {
124 let a: Vec<char> = a.chars().collect();
125 let b: Vec<char> = b.chars().collect();
126 let mut prev: Vec<usize> = (0..=b.len()).collect();
127 let mut cur = vec![0; b.len() + 1];
128 for (i, ca) in a.iter().enumerate() {
129 cur[0] = i + 1;
130 for (j, cb) in b.iter().enumerate() {
131 cur[j + 1] = if ca == cb {
132 prev[j]
133 } else {
134 1 + prev[j].min(prev[j + 1]).min(cur[j])
135 };
136 }
137 std::mem::swap(&mut prev, &mut cur);
138 }
139 prev[b.len()]
140}