1#[macro_use]
5pub(crate) mod humanize;
6
7use std::convert;
8use std::error;
9use std::fmt;
10
11#[cfg(feature = "serde")]
12use serde::{Deserialize, Serialize};
13
14use self::humanize::humanize_error;
15use crate::grammar::lexer::{self, Location, Tok};
16pub use crate::interpreter::runtime::RuntimeError;
17pub use crate::linker::LinkerError;
18use crate::semantics::QasmType;
19pub use crate::semantics::SemanticError;
20
21pub type ParseError = lalrpop_util::ParseError<Location, lexer::Tok, lexer::LexicalError<Location>>;
23
24pub type SrcAndErr<'src, E> = (&'src str, E);
26
27#[non_exhaustive]
69#[derive(Debug, Clone, PartialEq, Eq, Hash)]
70#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
71pub enum QasmSimError<'src> {
72 UnknownError(String),
74 InvalidToken {
76 source: &'src str,
78 lineno: usize,
80 startpos: usize,
82 endpos: Option<usize>,
84 token: Option<Tok>,
86 expected: Vec<String>,
88 },
89 UnexpectedEOF {
91 source: &'src str,
93 lineno: usize,
95 startpos: usize,
97 endpos: Option<usize>,
99 token: Option<Tok>,
101 expected: Vec<String>,
103 },
104 UnexpectedToken {
106 source: &'src str,
108 lineno: usize,
110 startpos: usize,
112 endpos: Option<usize>,
114 token: Option<Tok>,
116 expected: Vec<String>,
118 },
119 RedefinitionError {
121 source: &'src str,
123 symbol_name: String,
125 lineno: usize,
127 previous_lineno: usize,
129 },
130 LibraryNotFound {
132 source: &'src str,
134 libpath: String,
136 lineno: usize,
138 },
139 IndexOutOfBounds {
141 source: &'src str,
143 lineno: usize,
145 symbol_name: String,
147 index: usize,
149 size: usize,
151 },
152 SymbolNotFound {
154 source: &'src str,
156 lineno: usize,
158 symbol_name: String,
160 expected: QasmType,
162 },
163 WrongNumberOfParameters {
166 source: &'src str,
168 lineno: usize,
170 symbol_name: String,
172 are_registers: bool,
174 given: usize,
176 expected: usize,
178 },
179 UndefinedGate {
181 source: &'src str,
183 lineno: usize,
185 symbol_name: String,
187 },
188 TypeMismatch {
190 source: &'src str,
192 lineno: usize,
194 symbol_name: String,
196 expected: QasmType,
198 },
199 RegisterSizeMismatch {
201 source: &'src str,
203 lineno: usize,
205 symbol_name: String,
207 sizes: Vec<usize>,
209 },
210}
211
212impl fmt::Display for QasmSimError<'_> {
213 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
214 let mut buffer = String::new();
215 humanize_error(&mut buffer, self)?;
216 write!(f, "{}", buffer)
217 }
218}
219
220impl error::Error for QasmSimError<'_> {}
221
222impl convert::From<String> for QasmSimError<'_> {
223 fn from(err: String) -> Self {
224 QasmSimError::UnknownError(err)
225 }
226}
227
228impl<'src> From<SrcAndErr<'src, ParseError>> for QasmSimError<'src> {
229 fn from(src_and_err: SrcAndErr<'src, ParseError>) -> Self {
230 let (input, error) = src_and_err;
231 match error {
232 ParseError::InvalidToken { location } => {
233 let (source, lineno, startpos, endpos) = extract_line(location.0, None, input);
234 QasmSimError::InvalidToken {
235 source,
236 lineno,
237 startpos,
238 endpos,
239 token: None,
240 expected: Vec::new(),
241 }
242 }
243 ParseError::UnrecognizedEOF { location, expected } => {
244 let (source, lineno, startpos, endpos) = extract_line(location.0, None, input);
245 QasmSimError::UnexpectedEOF {
246 source,
247 lineno,
248 startpos,
249 endpos,
250 token: None,
251 expected,
252 }
253 }
254 ParseError::UnrecognizedToken { token, expected } => {
255 let location = token.0;
256 let endlocation = token.2;
257 let (source, lineno, startpos, endpos) =
258 extract_line(location.0, Some(endlocation.0), input);
259 QasmSimError::UnexpectedToken {
260 source,
261 lineno,
262 startpos,
263 endpos,
264 token: Some(token.1),
265 expected,
266 }
267 }
268 ParseError::ExtraToken { token } => {
269 let location = token.0;
270 let endlocation = token.2;
271 let (source, lineno, startpos, endpos) =
272 extract_line(location.0, Some(endlocation.0), input);
273 QasmSimError::UnexpectedToken {
274 source,
275 lineno,
276 startpos,
277 endpos,
278 token: Some(token.1),
279 expected: Vec::new(),
280 }
281 }
282 ParseError::User { error: lexer_error } => {
283 let location = lexer_error.location;
284 let (source, lineno, startpos, endpos) = extract_line(location.0, None, input);
285 QasmSimError::InvalidToken {
286 source,
288 lineno,
289 startpos,
290 endpos,
291 token: None,
292 expected: Vec::new(),
293 }
294 }
295 }
296 }
297}
298
299impl<'src> From<SrcAndErr<'src, RuntimeError>> for QasmSimError<'src> {
300 fn from(source_and_error: SrcAndErr<'src, RuntimeError>) -> Self {
301 let (input, error) = source_and_error;
302 match error {
303 RuntimeError::Other => QasmSimError::UnknownError(format!("{:?}", error)),
304 RuntimeError::RegisterSizeMismatch {
305 location,
306 symbol_name,
307 sizes,
308 } => {
309 let (source, lineno, _, _) = extract_line(location.0, None, input);
310 QasmSimError::RegisterSizeMismatch {
311 source,
312 lineno,
313 symbol_name,
314 sizes,
315 }
316 }
317 RuntimeError::TypeMismatch {
318 location,
319 symbol_name,
320 expected,
321 } => {
322 let (source, lineno, _, _) = extract_line(location.0, None, input);
323 QasmSimError::TypeMismatch {
324 source,
325 lineno,
326 symbol_name,
327 expected,
328 }
329 }
330 RuntimeError::UndefinedGate {
331 location,
332 symbol_name,
333 } => {
334 let (source, lineno, _, _) = extract_line(location.0, None, input);
335 QasmSimError::UndefinedGate {
336 source,
337 lineno,
338 symbol_name,
339 }
340 }
341 RuntimeError::WrongNumberOfParameters {
342 are_registers,
343 location,
344 symbol_name,
345 given,
346 expected,
347 } => {
348 let (source, lineno, _, _) = extract_line(location.0, None, input);
349 QasmSimError::WrongNumberOfParameters {
350 are_registers,
351 source,
352 symbol_name,
353 lineno,
354 expected,
355 given,
356 }
357 }
358 RuntimeError::SymbolNotFound {
359 location,
360 symbol_name,
361 expected,
362 } => {
363 let (source, lineno, _, _) = extract_line(location.0, None, input);
364 QasmSimError::SymbolNotFound {
365 source,
366 symbol_name,
367 lineno,
368 expected,
369 }
370 }
371 RuntimeError::IndexOutOfBounds {
372 location,
373 symbol_name,
374 index,
375 size,
376 } => {
377 let (source, lineno, _, _) = extract_line(location.0, None, input);
378 QasmSimError::IndexOutOfBounds {
379 source,
380 symbol_name,
381 lineno,
382 size,
383 index,
384 }
385 }
386 RuntimeError::SemanticError(semantic_error) => match semantic_error {
387 SemanticError::RedefinitionError {
388 symbol_name,
389 location,
390 previous_location,
391 } => {
392 let (source, lineno, _, _) = extract_line(location.0, None, input);
393 let (_, previous_lineno, _, _) = extract_line(previous_location.0, None, input);
394 QasmSimError::RedefinitionError {
395 source,
396 symbol_name,
397 lineno,
398 previous_lineno,
399 }
400 }
401 },
402 }
403 }
404}
405
406impl<'src> From<SrcAndErr<'src, LinkerError>> for QasmSimError<'src> {
407 fn from(source_and_error: SrcAndErr<'src, LinkerError>) -> Self {
408 let (input, error) = source_and_error;
409 match error {
410 LinkerError::LibraryNotFound { location, libpath } => {
411 let (source, lineno, _, _) = extract_line(location.0, None, input);
412 QasmSimError::LibraryNotFound {
413 source,
414 libpath,
415 lineno,
416 }
417 }
418 }
419 }
420}
421
422fn extract_line(
423 offset: usize,
424 endoffset: Option<usize>,
425 doc: &str,
426) -> (&str, usize, usize, Option<usize>) {
427 assert!(
428 offset <= doc.len(),
429 "linestart={} must in the range 0..=doc.len()={}",
430 offset,
431 doc.len()
432 );
433
434 let mut linecount = 1;
435 let mut start = 0;
436 let mut end = start + 1;
437 for (idx, character) in doc.chars().enumerate() {
438 if idx < offset && character == '\n' {
439 start = idx + 1;
440 linecount += 1;
441 }
442 if idx >= offset {
443 end = idx + 1;
444 if character == '\n' {
445 break;
446 }
447 }
448 }
449
450 if end < start {
451 end = doc.len();
452 }
453
454 let startpos = offset - start;
455 let endpos = endoffset.map(|endoffset| endoffset - start);
456
457 (&doc[start..end], linecount, startpos, endpos)
458}
459
460#[cfg(test)]
461mod test_into_doc_coords {
462 use indoc::indoc;
463
464 use super::extract_line;
465
466 macro_rules! test_get_line_src {
467 ($source:expr, $( $name:ident: $offset:expr, $offsetend:expr => $expected:expr ),*) => {
468 $(
469 #[test]
470 fn $name() {
471 assert_eq!(extract_line($offset, $offsetend, &$source), $expected);
472 }
473 )*
474 };
475 }
476
477 test_get_line_src!(indoc!("
478 line 1
479 line 2
480 line 3"
481 ),
482 test_beginning_of_source: 0, None => ("line 1\n", 1, 0, None),
483 test_middle_of_source: 7, None => ("line 2\n", 2, 0, None),
484 test_last_character: 20, None => ("line 3", 3, 6, None)
485 );
486}