1use crate::sdk::{token::TokenType, TokenImpl};
2use std::fmt::Write;
3
4#[derive(Debug)]
6pub struct ParseHook<H, P> {
7 pub pt: P,
8 pub val: Option<H>,
9}
10
11impl<H, P> ParseHook<H, P> {
12 pub fn take_unchecked(&mut self) -> H {
17 self.val.take().unwrap_or_else(||panic!("ParseHook::take() called on None. Make sure your parse hook function is not taking the value twice."))
18 }
19}
20
21#[derive(Debug)]
23pub struct MergedListTail<A, P> {
24 pub has_first: bool,
28 pub vals: Vec<P>,
30 pub asts: Vec<A>,
32}
33
34impl<A, P> IntoIterator for MergedListTail<A, P> {
35 type Item = (A, P);
36 type IntoIter = std::iter::Zip<std::vec::IntoIter<A>, std::vec::IntoIter<P>>;
37
38 fn into_iter(self) -> Self::IntoIter {
40 self.asts.into_iter().zip(self.vals)
41 }
42}
43
44#[macro_export]
61macro_rules! merge_list_tail {
62 ($pt:ident, $first:ident, $rest:ident, $rest_member:ident) => {{
63 let mut vals = vec![$pt.$first.as_ref()];
64 let mut asts = vec![&$pt.ast.$first];
65 for x in &mut $pt.$rest {
66 vals.push(x.$rest_member.as_ref());
67 asts.push(&x.ast.$rest_member);
68 }
69 $crate::sdk::MergedListTail {
70 has_first: true,
71 vals,
72 asts,
73 }
74 }};
75 (mut $pt:ident, $first:ident, $rest:ident, $rest_member:ident) => {{
76 let mut vals = vec![$pt.$first.as_mut()];
77 let mut asts = vec![&$pt.ast.$first];
78 for x in &mut $pt.$rest {
79 vals.push(x.$rest_member.as_mut());
80 asts.push(&x.ast.$rest_member);
81 }
82 $crate::sdk::MergedListTail {
83 has_first: true,
84 vals,
85 asts,
86 }
87 }};
88}
89
90#[macro_export]
105macro_rules! merge_list_tail_optional_first {
106 ($pt:ident, $first:ident, $rest:ident, $rest_member:ident) => {{
107 let (has_first, mut vals, mut asts) = match &$pt.$first {
108 Some(x) => (
109 true,
110 vec![x.as_ref()],
111 vec![$pt.ast.$first.as_ref().unwrap()],
112 ),
113 None => (false, vec![], vec![]),
114 };
115 for x in &$pt.$rest {
116 vals.push(x.$rest_member.as_ref());
117 asts.push(&x.ast.$rest_member);
118 }
119 $crate::sdk::MergedListTail {
120 has_first,
121 vals,
122 asts,
123 }
124 }};
125 (mut $pt:ident, $first:ident, $rest:ident, $rest_member:ident) => {{
126 let (has_first, mut vals, mut asts) = match &mut $pt.$first {
127 Some(x) => (
128 true,
129 vec![x.as_mut()],
130 vec![$pt.ast.$first.as_ref().unwrap()],
131 ),
132 None => (false, vec![], vec![]),
133 };
134 for x in &mut $pt.$rest {
135 vals.push(x.$rest_member.as_mut());
136 asts.push(&x.ast.$rest_member);
137 }
138 $crate::sdk::MergedListTail {
139 has_first,
140 vals,
141 asts,
142 }
143 }};
144}
145
146#[derive(PartialEq, Debug, Clone)]
148pub struct Error {
149 pub msg: String,
151 pub help: Option<String>,
153 pub pos: (usize, usize),
155}
156
157impl Error {
158 pub fn global(msg: String, help: String) -> Self {
159 Self {
160 msg,
161 help: Some(help),
162 pos: (0, 1),
163 }
164 }
165
166 pub fn from_token<T>(token: &TokenImpl<T>, msg: String, help: String) -> Self
167 where
168 T: TokenType,
169 {
170 Self {
171 msg,
172 help: Some(help),
173 pos: token.pos,
174 }
175 }
176
177 pub fn from_token_without_help<T>(token: &TokenImpl<T>, msg: String) -> Self
178 where
179 T: TokenType,
180 {
181 Self {
182 msg,
183 help: None,
184 pos: token.pos,
185 }
186 }
187
188 pub fn pretty(&self, source: &str, context: usize) -> Result<String, std::fmt::Error> {
189 let mut output = String::new();
190 let (l, c) = self.to_line_col(source);
191 let m = &self.msg;
192 writeln!(output, "error: {m}")?;
193 writeln!(output, " --> {l}:{c}")?;
194 writeln!(output, " |")?;
195
196 let start = if l > context { l - context } else { 1 };
197 for (i, line_str) in source
198 .lines()
199 .skip(start - 1)
200 .take(context * 2 + 1)
201 .enumerate()
202 {
203 let current = i + start;
204 writeln!(output, "{current:3} | {line_str}")?;
205 let help = self
206 .help
207 .as_ref()
208 .map(|s| format!(" help: {}", s))
209 .unwrap_or_default();
210 if current == l {
211 writeln!(
212 output,
213 "{:3} | {:>width$}{t}{help}",
214 "",
215 "",
216 width = c - 1,
217 t = "^".repeat(self.pos.1 - self.pos.0)
218 )?;
219 }
220 }
221
222 Ok(output)
223 }
224
225 fn to_line_col(&self, content: &str) -> (usize, usize) {
226 let (start, _) = self.pos;
227 let mut cur = 0;
228 let mut l = 1;
229 for line in content.split('\n') {
230 let line_len = line.len() + 1;
231 if cur + line_len > start {
232 return (l, start - cur + 1);
233 }
234 cur += line_len;
235 l += 1;
236 }
237 (l, 0)
238 }
239}