simplicity/human_encoding/
error.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! Parsing Errors
4
5use santiago::lexer::Lexeme;
6use std::collections::BTreeMap;
7use std::sync::{Arc, Mutex};
8use std::{error, fmt, iter};
9
10use crate::types;
11
12use super::Position;
13
14/// A set of errors found in a human-readable encoding of a Simplicity program.
15#[derive(Clone, Debug, Default)]
16pub struct ErrorSet {
17    context: Option<Arc<str>>,
18    line_map: Arc<Mutex<Vec<usize>>>,
19    errors: BTreeMap<Option<Position>, Vec<Error>>,
20}
21
22impl<T> From<santiago::parser::ParseError<T>> for ErrorSet {
23    fn from(e: santiago::parser::ParseError<T>) -> Self {
24        let lex = e.at.map(|rc| (*rc).clone());
25        match lex.as_ref().map(|lex| &lex.position).map(Position::from) {
26            Some(pos) => ErrorSet::single(pos, Error::ParseFailed(lex)),
27            None => ErrorSet::single_no_position(Error::ParseFailed(lex)),
28        }
29    }
30}
31
32impl From<santiago::lexer::LexerError> for ErrorSet {
33    fn from(e: santiago::lexer::LexerError) -> Self {
34        ErrorSet::single(e.position, Error::LexFailed(e.message))
35    }
36}
37
38impl ErrorSet {
39    /// Constructs a new empty error set.
40    pub fn new() -> Self {
41        ErrorSet::default()
42    }
43
44    /// Returns the first (and presumably most important) error in the set, if it
45    /// is non-empty, along with its position.
46    pub fn first_error(&self) -> Option<(Option<Position>, &Error)> {
47        self.errors.iter().next().map(|(a, b)| (*a, &b[0]))
48    }
49
50    /// Return an iterator over the errors in the error set.
51    pub fn iter(&self) -> impl Iterator<Item = &Error> {
52        self.errors.values().flatten()
53    }
54
55    /// Constructs a new error set with a single error in it.
56    pub fn single<P: Into<Position>, E: Into<Error>>(position: P, err: E) -> Self {
57        let mut errors = BTreeMap::default();
58        errors.insert(Some(position.into()), vec![err.into()]);
59        ErrorSet {
60            context: None,
61            line_map: Arc::new(Mutex::new(vec![])),
62            errors,
63        }
64    }
65
66    /// Constructs a new error set with a single error in it.
67    pub fn single_no_position<E: Into<Error>>(err: E) -> Self {
68        let mut errors = BTreeMap::default();
69        errors.insert(None, vec![err.into()]);
70        ErrorSet {
71            context: None,
72            line_map: Arc::new(Mutex::new(vec![])),
73            errors,
74        }
75    }
76
77    /// Adds an error to the error set.
78    pub fn add<P: Into<Position>, E: Into<Error>>(&mut self, position: P, err: E) {
79        self.errors
80            .entry(Some(position.into()))
81            .or_default()
82            .push(err.into());
83    }
84
85    /// Adds an error to the error set.
86    pub fn add_no_position<E: Into<Error>>(&mut self, err: E) {
87        self.errors.entry(None).or_default().push(err.into());
88    }
89
90    /// Merges another set of errors into the current set.
91    ///
92    /// # Panics
93    ///
94    /// Panics if the two sets have different contexts attached.
95    pub fn merge(&mut self, other: &Self) {
96        match (self.context.as_ref(), other.context.as_ref()) {
97            (None, None) => {}
98            (Some(_), None) => {}
99            (None, Some(b)) => self.context = Some(Arc::clone(b)),
100            (Some(a), Some(b)) => {
101                assert_eq!(a, b, "cannot merge error sets for different source input");
102            }
103        };
104
105        for (pos, errs) in &other.errors {
106            self.errors
107                .entry(*pos)
108                .or_default()
109                .extend(errs.iter().cloned());
110        }
111    }
112
113    /// Attaches the input code to the error set, so that error messages can include
114    /// line numbers etc.
115    ///
116    /// # Panics
117    ///
118    /// Panics if it is called twice on the same error set. You should call this once
119    /// with the complete input code.
120    pub fn add_context(&mut self, s: Arc<str>) {
121        if self.context.is_some() {
122            panic!("tried to add context to the same error context twice");
123        }
124        self.context = Some(s);
125    }
126
127    /// Returns a boolean indicating whether the set is empty.
128    pub fn is_empty(&self) -> bool {
129        self.errors.is_empty()
130    }
131
132    /// Returns the number of errors currently in the set.
133    pub fn len(&self) -> usize {
134        self.errors.len()
135    }
136
137    /// Converts the error set into a result.
138    ///
139    /// If the set is empty, returns Ok with the given value. Otherwise
140    /// returns Err with itself.
141    pub fn into_result<T>(self, ok: T) -> Result<T, Self> {
142        if self.is_empty() {
143            Ok(ok)
144        } else {
145            Err(self)
146        }
147    }
148
149    /// Converts the error set into a result.
150    ///
151    /// If the set is empty, returns Ok with the result of calling the given closure.
152    /// Otherwise returns Err with itself.
153    pub fn into_result_with<T, F: FnOnce() -> T>(self, okfn: F) -> Result<T, Self> {
154        if self.is_empty() {
155            Ok(okfn())
156        } else {
157            Err(self)
158        }
159    }
160}
161
162impl error::Error for ErrorSet {
163    fn cause(&self) -> Option<&(dyn error::Error + 'static)> {
164        match self.first_error()?.1 {
165            Error::Bad2ExpNumber(..) => None,
166            Error::BadWordLength { .. } => None,
167            Error::EntropyInsufficient { .. } => None,
168            Error::EntropyTooMuch { .. } => None,
169            Error::HoleAtCommitTime { .. } => None,
170            Error::HoleFilledAtCommitTime => None,
171            Error::NameIllegal(_) => None,
172            Error::NameIncomplete(_) => None,
173            Error::NameMissing(_) => None,
174            Error::NameRepeated(_) => None,
175            Error::NoMain => None,
176            Error::ParseFailed(_) => None,
177            Error::LexFailed(_) => None,
178            Error::NumberOutOfRange(_) => None,
179            Error::TypeCheck(ref e) => Some(e),
180            Error::Undefined(_) => None,
181            Error::UnknownJet(_) => None,
182            Error::WitnessDisconnectRepeated { .. } => None,
183        }
184    }
185}
186
187impl fmt::Display for ErrorSet {
188    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
189        let mut line_map = self.line_map.lock().unwrap();
190        if line_map.is_empty() {
191            if let Some(ref s) = self.context {
192                *line_map = iter::repeat(0)
193                    .take(2)
194                    .chain(
195                        s.char_indices()
196                            .filter_map(|(n, ch)| if ch == '\n' { Some(n) } else { None }),
197                    )
198                    .collect();
199            }
200        }
201
202        for (pos, errs) in &self.errors {
203            if let Some(pos) = pos {
204                for err in errs {
205                    if let Some(ref s) = self.context {
206                        let end = line_map.get(pos.line + 1).copied().unwrap_or(s.len());
207                        let line = &s[line_map[pos.line] + 1..end];
208                        writeln!(f, "{:5} | {}", pos.line, line)?;
209                        writeln!(f, "      | {:>width$}", "^", width = pos.column)?;
210                        writeln!(f, "      \\-- {}", err)?;
211                        writeln!(f)?;
212                    } else {
213                        writeln!(f, "{:4}:{:2}: {}", pos.line, pos.column, err,)?;
214                        writeln!(f)?;
215                    }
216                }
217            } else {
218                for err in errs {
219                    writeln!(f, "Error: {}", err)?;
220                }
221            }
222        }
223        Ok(())
224    }
225}
226
227/// An individual error.
228///
229/// Generally this structure should not be used on its own, but only wrapped in an
230/// [`ErrorSet`]. This is because in the human-readable encoding errors it is usually
231/// possible to continue past individual errors, and the user would prefer to see as
232/// many as possible at once.
233#[derive(Clone, Debug)]
234pub enum Error {
235    /// A number of the form 2^y was used as a type but y was not an allowed value
236    Bad2ExpNumber(u32),
237    /// A constant word had a length which was not an allowable power of 2
238    BadWordLength { bit_length: usize },
239    /// A "fail" node was provided with less than 128 bits of entropy
240    EntropyInsufficient { bit_length: usize },
241    /// A "fail" node was provided with more than 512 bits of entropy
242    EntropyTooMuch { bit_length: usize },
243    /// When converting to a `CommitNode`, there were unfilled holes which prevent
244    /// us from knowing the whole program.
245    HoleAtCommitTime {
246        name: Arc<str>,
247        arrow_source: Arc<types::Incomplete>,
248        arrow_target: Arc<types::Incomplete>,
249    },
250    /// When converting to a `CommitNode`, a disconnect node had an actual node rather
251    /// than a hole.
252    HoleFilledAtCommitTime,
253    /// An expression name was not allowed to be used as a name.
254    NameIllegal(Arc<str>),
255    /// An expression was given a type, but no actual expression was provided.
256    NameIncomplete(Arc<str>),
257    /// An expression was referenced but did not refer to anything.
258    NameMissing(Arc<str>),
259    /// An expression name was used for multiple expressions.
260    NameRepeated(Arc<str>),
261    /// Program did not have a `main` expression
262    NoMain,
263    /// Parsing failed (the parser provides us some extra information, but beyond
264    /// the line and column, it does not seem very useful to a user, so we drop it).
265    ParseFailed(Option<Lexeme>),
266    /// Lexing failed; here santiago provides us an error message which is useful
267    LexFailed(String),
268    /// A number was parsed in some context but was out of range.
269    NumberOutOfRange(String),
270    /// Simplicity type-checking error
271    TypeCheck(types::Error),
272    /// Expression referred to an undefined symbol
273    Undefined(String),
274    /// A given jet is not a known jet
275    UnknownJet(String),
276    /// A witness or disconnect node was accessible from multiple paths.
277    WitnessDisconnectRepeated { name: Arc<str>, count: usize },
278}
279
280impl From<types::Error> for Error {
281    fn from(e: types::Error) -> Self {
282        Error::TypeCheck(e)
283    }
284}
285
286impl fmt::Display for Error {
287    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
288        match *self {
289            Error::BadWordLength { bit_length } => {
290                write!(f, "word length {} is not a valid power of 2", bit_length)
291            }
292            Error::Bad2ExpNumber(exp) => {
293                write!(f, "types may be 2^n for n a power of 2, but not 2^{}", exp)
294            }
295            Error::EntropyInsufficient { bit_length } => write!(
296                f,
297                "fail node has insufficient entropy ({} bits, need 128)",
298                bit_length
299            ),
300            Error::EntropyTooMuch { bit_length } => write!(
301                f,
302                "fail node has too much entropy ({} bits, max 512)",
303                bit_length
304            ),
305            Error::HoleAtCommitTime {
306                ref name,
307                ref arrow_source,
308                ref arrow_target,
309            } => write!(
310                f,
311                "unfilled hole ?{} at commitment time; type arrow {} -> {}",
312                name, arrow_source, arrow_target,
313            ),
314            Error::HoleFilledAtCommitTime => {
315                f.write_str("disconnect node has a non-hole child at commit time")
316            }
317            Error::NameIllegal(ref s) => {
318                write!(f, "name `{}` is not allowed in this context", s)
319            }
320            Error::NameIncomplete(ref s) => write!(f, "name `{}` has no expression", s),
321            Error::NameMissing(ref s) => {
322                write!(f, "name `{}` is referred to but does not exist", s)
323            }
324            Error::NameRepeated(ref s) => write!(f, "name `{}` occured mulitple times", s),
325            Error::NoMain => f.write_str("program does not define `main`"),
326            Error::NumberOutOfRange(ref n) => {
327                write!(f, "number {} was out of allowable range", n)
328            }
329            Error::ParseFailed(None) => f.write_str("could not parse"),
330            Error::ParseFailed(Some(ref lex)) => write!(f, "could not parse `{}`", lex.raw),
331            Error::LexFailed(ref msg) => write!(f, "could not parse: {}", msg),
332            Error::TypeCheck(ref e) => fmt::Display::fmt(e, f),
333            Error::Undefined(ref s) => write!(f, "reference to undefined symbol `{}`", s),
334            Error::UnknownJet(ref s) => write!(f, "unknown jet `{}`", s),
335            Error::WitnessDisconnectRepeated { ref name, count } => write!(
336                f,
337                "witness/disconnect node {} was accessible by {} distinct paths from the same root",
338                name, count,
339            ),
340        }
341    }
342}