1use std::fmt;
2use std::num::NonZeroUsize;
3use std::sync::Arc;
4
5use simplicity::hashes::{sha256, Hash, HashEngine};
6use simplicity::{elements, Cmr};
7
8use crate::parse::{MatchPattern, Rule};
9use crate::str::{AliasName, FunctionName, Identifier, JetName, ModuleName, WitnessName};
10use crate::types::{ResolvedType, UIntType};
11
12#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
16pub struct Position {
17 pub line: NonZeroUsize,
21 pub col: NonZeroUsize,
25}
26
27impl Position {
28 #[cfg(feature = "arbitrary")]
30 pub(crate) const DUMMY: Self = Self::new(1, 1);
31
32 pub const fn new(line: usize, col: usize) -> Self {
38 if line == 0 {
39 panic!("Line must not be zero");
40 }
41 let line = unsafe { NonZeroUsize::new_unchecked(line) };
43 if col == 0 {
44 panic!("Column must not be zero");
45 }
46 let col = unsafe { NonZeroUsize::new_unchecked(col) };
48 Self { line, col }
49 }
50}
51
52#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
58pub struct Span {
59 pub start: Position,
61 pub end: Position,
63}
64
65impl Span {
66 #[cfg(feature = "arbitrary")]
68 pub(crate) const DUMMY: Self = Self::new(Position::DUMMY, Position::DUMMY);
69
70 pub const fn new(start: Position, end: Position) -> Self {
76 assert!(
79 start.line.get() <= end.line.get(),
80 "Start cannot come after end"
81 );
82 assert!(
83 start.line.get() < end.line.get() || start.col.get() <= end.col.get(),
84 "Start cannot come after end"
85 );
86 Self { start, end }
87 }
88
89 pub const fn is_multiline(&self) -> bool {
91 self.start.line.get() < self.end.line.get()
92 }
93
94 pub fn cmr(&self) -> Cmr {
96 let mut hasher = sha256::HashEngine::default();
97 hasher.input(&self.start.line.get().to_be_bytes());
98 hasher.input(&self.start.col.get().to_be_bytes());
99 hasher.input(&self.end.line.get().to_be_bytes());
100 hasher.input(&self.end.col.get().to_be_bytes());
101 let hash = sha256::Hash::from_engine(hasher);
102 Cmr::from_byte_array(hash.to_byte_array())
103 }
104
105 pub fn to_slice<'a>(&self, file: &'a str) -> Option<&'a str> {
109 let mut current_line = 1;
110 let mut current_col = 1;
111 let mut start_index = None;
112
113 for (i, c) in file.char_indices() {
114 if current_line == self.start.line.get() && current_col == self.start.col.get() {
115 start_index = Some(i);
116 }
117 if current_line == self.end.line.get() && current_col == self.end.col.get() {
118 let start_index = start_index.expect("start comes before end");
119 let end_index = i;
120 return Some(&file[start_index..end_index]);
121 }
122 if c == '\n' {
123 current_line += 1;
124 current_col = 1;
125 } else {
126 current_col += 1;
127 }
128 }
129
130 None
131 }
132}
133
134impl<'a> From<&'a pest::iterators::Pair<'_, Rule>> for Span {
135 fn from(pair: &'a pest::iterators::Pair<Rule>) -> Self {
136 let (line, col) = pair.line_col();
137 let start = Position::new(line, col);
138 let (line, col) = pair.as_span().end_pos().line_col();
142 let end = Position::new(line, col);
143 Self::new(start, end)
144 }
145}
146
147impl From<&str> for Span {
148 fn from(s: &str) -> Self {
149 let start = Position::new(1, 1);
150 let end_line = std::cmp::max(1, s.lines().count());
151 let end_col = std::cmp::max(1, s.lines().next_back().unwrap_or("").len());
152 let end = Position::new(end_line, end_col);
153 debug_assert!(start.line <= end.line);
154 debug_assert!(start.line < end.line || start.col <= end.col);
155 Span::new(start, end)
156 }
157}
158
159#[cfg(feature = "arbitrary")]
160impl<'a> arbitrary::Arbitrary<'a> for Span {
161 fn arbitrary(_: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
162 Ok(Self::DUMMY)
163 }
164}
165
166pub trait WithSpan<T> {
168 fn with_span<S: Into<Span>>(self, span: S) -> Result<T, RichError>;
170}
171
172impl<T, E: Into<Error>> WithSpan<T> for Result<T, E> {
173 fn with_span<S: Into<Span>>(self, span: S) -> Result<T, RichError> {
174 self.map_err(|e| e.into().with_span(span.into()))
175 }
176}
177
178pub trait WithFile<T> {
180 fn with_file<F: Into<Arc<str>>>(self, file: F) -> Result<T, RichError>;
184}
185
186impl<T> WithFile<T> for Result<T, RichError> {
187 fn with_file<F: Into<Arc<str>>>(self, file: F) -> Result<T, RichError> {
188 self.map_err(|e| e.with_file(file.into()))
189 }
190}
191
192#[derive(Debug, Clone, Eq, PartialEq, Hash)]
196pub struct RichError {
197 error: Error,
199 span: Span,
201 file: Option<Arc<str>>,
205}
206
207impl RichError {
208 pub fn new(error: Error, span: Span) -> RichError {
210 RichError {
211 error,
212 span,
213 file: None,
214 }
215 }
216
217 pub fn with_file(self, file: Arc<str>) -> Self {
221 Self {
222 error: self.error,
223 span: self.span,
224 file: Some(file),
225 }
226 }
227}
228
229impl fmt::Display for RichError {
230 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
231 match self.file {
232 Some(ref file) if !file.is_empty() => {
233 let start_line_index = self.span.start.line.get() - 1;
234 let n_spanned_lines = self.span.end.line.get() - start_line_index;
235 let line_num_width = self.span.end.line.get().to_string().len();
236 writeln!(f, "{:width$} |", " ", width = line_num_width)?;
237
238 let mut lines = file.lines().skip(start_line_index).peekable();
239 let start_line_len = lines.peek().map(|l| l.len()).unwrap_or(0);
240
241 for (relative_line_index, line_str) in lines.take(n_spanned_lines).enumerate() {
242 let line_num = start_line_index + relative_line_index + 1;
243 writeln!(f, "{line_num:line_num_width$} | {line_str}")?;
244 }
245
246 let (underline_start, underline_length) = match self.span.is_multiline() {
247 true => (0, start_line_len),
248 false => (
249 self.span.start.col.get(),
250 self.span.end.col.get() - self.span.start.col.get(),
251 ),
252 };
253 write!(f, "{:width$} |", " ", width = line_num_width)?;
254 write!(f, "{:width$}", " ", width = underline_start)?;
255 write!(f, "{:^<width$} ", "", width = underline_length)?;
256 write!(f, "{}", self.error)
257 }
258 _ => {
259 write!(f, "{}", self.error)
260 }
261 }
262 }
263}
264
265impl std::error::Error for RichError {}
266
267impl From<RichError> for Error {
268 fn from(error: RichError) -> Self {
269 error.error
270 }
271}
272
273impl From<RichError> for String {
274 fn from(error: RichError) -> Self {
275 error.to_string()
276 }
277}
278
279impl From<pest::error::Error<Rule>> for RichError {
280 fn from(error: pest::error::Error<Rule>) -> Self {
281 let description = error.variant.message().to_string();
282 let (start, end) = match error.line_col {
283 pest::error::LineColLocation::Pos((line, col)) => {
284 (Position::new(line, col), Position::new(line, col + 1))
285 }
286 pest::error::LineColLocation::Span((line, col), (line_end, col_end)) => {
287 (Position::new(line, col), Position::new(line_end, col_end))
288 }
289 };
290 let span = Span::new(start, end);
291 Self::new(Error::Grammar(description), span)
292 }
293}
294
295#[derive(Debug, Clone, Eq, PartialEq, Hash)]
299pub enum Error {
300 ListBoundPow2(usize),
301 BitStringPow2(usize),
302 HexStringLen(usize),
303 ForWhileWidthPow2(usize),
304 CannotParse(String),
305 Grammar(String),
306 IncompatibleMatchArms(MatchPattern, MatchPattern),
307 CannotCompile(String),
311 JetDoesNotExist(JetName),
312 InvalidCast(ResolvedType, ResolvedType),
313 MainNoInputs,
314 MainNoOutput,
315 MainRequired,
316 FunctionRedefined(FunctionName),
317 FunctionUndefined(FunctionName),
318 InvalidNumberOfArguments(usize, usize),
319 FunctionNotFoldable(FunctionName),
320 FunctionNotLoopable(FunctionName),
321 ExpressionUnexpectedType(ResolvedType),
322 ExpressionTypeMismatch(ResolvedType, ResolvedType),
323 ExpressionNotConstant,
324 IntegerOutOfBounds(UIntType),
325 UndefinedVariable(Identifier),
326 UndefinedAlias(AliasName),
327 VariableReuseInPattern(Identifier),
328 WitnessReused(WitnessName),
329 WitnessTypeMismatch(WitnessName, ResolvedType, ResolvedType),
330 WitnessReassigned(WitnessName),
331 WitnessOutsideMain,
332 ModuleRequired(ModuleName),
333 ModuleRedefined(ModuleName),
334 ArgumentMissing(WitnessName),
335 ArgumentTypeMismatch(WitnessName, ResolvedType, ResolvedType),
336}
337
338#[rustfmt::skip]
339impl fmt::Display for Error {
340 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
341 match self {
342 Error::ListBoundPow2(bound) => write!(
343 f,
344 "Expected a power of two greater than one (2, 4, 8, 16, 32, ...) as list bound, found {bound}"
345 ),
346 Error::BitStringPow2(len) => write!(
347 f,
348 "Expected a valid bit string length (1, 2, 4, 8, 16, 32, 64, 128, 256), found {len}"
349 ),
350 Error::HexStringLen(len) => write!(
351 f,
352 "Expected an even hex string length (0, 2, 4, 6, 8, ...), found {len}"
353 ),
354 Error::ForWhileWidthPow2(bit_width) => write!(
355 f,
356 "Expected a power of two (1, 2, 4, 8, 16, ...) as for-while bit width, found {bit_width}"
357 ),
358 Error::CannotParse(description) => write!(
359 f,
360 "Cannot parse: {description}"
361 ),
362 Error::Grammar(description) => write!(
363 f,
364 "Grammar error: {description}"
365 ),
366 Error::IncompatibleMatchArms(pattern1, pattern2) => write!(
367 f,
368 "Match arm `{pattern1}` is incompatible with arm `{pattern2}`"
369 ),
370 Error::CannotCompile(description) => write!(
371 f,
372 "Failed to compile to Simplicity: {description}"
373 ),
374 Error::JetDoesNotExist(name) => write!(
375 f,
376 "Jet `{name}` does not exist"
377 ),
378 Error::InvalidCast(source, target) => write!(
379 f,
380 "Cannot cast values of type `{source}` as values of type `{target}`"
381 ),
382 Error::MainNoInputs => write!(
383 f,
384 "Main function takes no input parameters"
385 ),
386 Error::MainNoOutput => write!(
387 f,
388 "Main function produces no output"
389 ),
390 Error::MainRequired => write!(
391 f,
392 "Main function is required"
393 ),
394 Error::FunctionRedefined(name) => write!(
395 f,
396 "Function `{name}` was defined multiple times"
397 ),
398 Error::FunctionUndefined(name) => write!(
399 f,
400 "Function `{name}` was called but not defined"
401 ),
402 Error::InvalidNumberOfArguments(expected, found) => write!(
403 f,
404 "Expected {expected} arguments, found {found} arguments"
405 ),
406 Error::FunctionNotFoldable(name) => write!(
407 f,
408 "Expected a signature like `fn {name}(element: E, accumulator: A) -> A` for a fold"
409 ),
410 Error::FunctionNotLoopable(name) => write!(
411 f,
412 "Expected a signature like `fn {name}(accumulator: A, context: C, counter u{{1,2,4,8,16}}) -> Either<B, A>` for a for-while loop"
413 ),
414 Error::ExpressionUnexpectedType(ty) => write!(
415 f,
416 "Expected expression of type `{ty}`; found something else"
417 ),
418 Error::ExpressionTypeMismatch(expected, found) => write!(
419 f,
420 "Expected expression of type `{expected}`, found type `{found}`"
421 ),
422 Error::ExpressionNotConstant => write!(
423 f,
424 "Expression cannot be evaluated at compile time"
425 ),
426 Error::IntegerOutOfBounds(ty) => write!(
427 f,
428 "Value is out of bounds for type `{ty}`"
429 ),
430 Error::UndefinedVariable(identifier) => write!(
431 f,
432 "Variable `{identifier}` is not defined"
433 ),
434 Error::UndefinedAlias(identifier) => write!(
435 f,
436 "Type alias `{identifier}` is not defined"
437 ),
438 Error::VariableReuseInPattern(identifier) => write!(
439 f,
440 "Variable `{identifier}` is used twice in the pattern"
441 ),
442 Error::WitnessReused(name) => write!(
443 f,
444 "Witness `{name}` has been used before somewhere in the program"
445 ),
446 Error::WitnessTypeMismatch(name, declared, assigned) => write!(
447 f,
448 "Witness `{name}` was declared with type `{declared}` but its assigned value is of type `{assigned}`"
449 ),
450 Error::WitnessReassigned(name) => write!(
451 f,
452 "Witness `{name}` has already been assigned a value"
453 ),
454 Error::WitnessOutsideMain => write!(
455 f,
456 "Witness expressions are not allowed outside the `main` function"
457 ),
458 Error::ModuleRequired(name) => write!(
459 f,
460 "Required module `{name}` is missing"
461 ),
462 Error::ModuleRedefined(name) => write!(
463 f,
464 "Module `{name}` is defined twice"
465 ),
466 Error::ArgumentMissing(name) => write!(
467 f,
468 "Parameter `{name}` is missing an argument"
469 ),
470 Error::ArgumentTypeMismatch(name, declared, assigned) => write!(
471 f,
472 "Parameter `{name}` was declared with type `{declared}` but its assigned argument is of type `{assigned}`"
473 ),
474 }
475 }
476}
477
478impl std::error::Error for Error {}
479
480impl Error {
481 pub fn with_span(self, span: Span) -> RichError {
483 RichError::new(self, span)
484 }
485}
486
487impl From<elements::hex::Error> for Error {
488 fn from(error: elements::hex::Error) -> Self {
489 Self::CannotParse(error.to_string())
490 }
491}
492
493impl From<std::num::ParseIntError> for Error {
494 fn from(error: std::num::ParseIntError) -> Self {
495 Self::CannotParse(error.to_string())
496 }
497}
498
499impl From<crate::num::ParseIntError> for Error {
500 fn from(error: crate::num::ParseIntError) -> Self {
501 Self::CannotParse(error.to_string())
502 }
503}
504
505impl From<simplicity::types::Error> for Error {
506 fn from(error: simplicity::types::Error) -> Self {
507 Self::CannotCompile(error.to_string())
508 }
509}
510
511#[cfg(test)]
512mod tests {
513 use super::*;
514
515 const FILE: &str = r#"let a1: List<u32, 5> = None;
516let x: u32 = Left(
517 Right(0)
518);"#;
519 const EMPTY_FILE: &str = "";
520
521 #[test]
522 fn display_single_line() {
523 let error = Error::ListBoundPow2(5)
524 .with_span(Span::new(Position::new(1, 14), Position::new(1, 20)))
525 .with_file(Arc::from(FILE));
526 let expected = r#"
527 |
5281 | let a1: List<u32, 5> = None;
529 | ^^^^^^ Expected a power of two greater than one (2, 4, 8, 16, 32, ...) as list bound, found 5"#;
530 assert_eq!(&expected[1..], &error.to_string());
531 }
532
533 #[test]
534 fn display_multi_line() {
535 let error = Error::CannotParse(
536 "Expected value of type `u32`, got `Either<Either<_, u32>, _>`".to_string(),
537 )
538 .with_span(Span::new(Position::new(2, 21), Position::new(4, 2)))
539 .with_file(Arc::from(FILE));
540 let expected = r#"
541 |
5422 | let x: u32 = Left(
5433 | Right(0)
5444 | );
545 | ^^^^^^^^^^^^^^^^^^ Cannot parse: Expected value of type `u32`, got `Either<Either<_, u32>, _>`"#;
546 assert_eq!(&expected[1..], &error.to_string());
547 }
548
549 #[test]
550 fn display_entire_file() {
551 let error = Error::CannotParse("This span covers the entire file".to_string())
552 .with_span(Span::from(FILE))
553 .with_file(Arc::from(FILE));
554 let expected = r#"
555 |
5561 | let a1: List<u32, 5> = None;
5572 | let x: u32 = Left(
5583 | Right(0)
5594 | );
560 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Cannot parse: This span covers the entire file"#;
561 assert_eq!(&expected[1..], &error.to_string());
562 }
563
564 #[test]
565 fn display_no_file() {
566 let error = Error::CannotParse("This error has no file".to_string())
567 .with_span(Span::from(EMPTY_FILE));
568 let expected = "Cannot parse: This error has no file";
569 assert_eq!(&expected, &error.to_string());
570
571 let error = Error::CannotParse("This error has no file".to_string())
572 .with_span(Span::new(Position::new(1, 1), Position::new(2, 2)));
573 assert_eq!(&expected, &error.to_string());
574 }
575
576 #[test]
577 fn display_empty_file() {
578 let error = Error::CannotParse("This error has an empty file".to_string())
579 .with_span(Span::from(EMPTY_FILE))
580 .with_file(Arc::from(EMPTY_FILE));
581 let expected = "Cannot parse: This error has an empty file";
582 assert_eq!(&expected, &error.to_string());
583
584 let error = Error::CannotParse("This error has an empty file".to_string())
585 .with_span(Span::new(Position::new(1, 1), Position::new(2, 2)))
586 .with_file(Arc::from(EMPTY_FILE));
587 assert_eq!(&expected, &error.to_string());
588 }
589}