| 1 | use std::ops::Range; | 
| 2 | use thiserror::Error; | 
|---|
| 3 |  | 
|---|
| 4 | /// Error containing information about an error encountered by the Fluent Parser. | 
|---|
| 5 | /// | 
|---|
| 6 | /// Errors in Fluent Parser are non-fatal, and the syntax has been | 
|---|
| 7 | /// designed to allow for strong recovery. | 
|---|
| 8 | /// | 
|---|
| 9 | /// In result [`ParserError`] is designed to point at the slice of | 
|---|
| 10 | /// the input that is most likely to be a complete fragment from after | 
|---|
| 11 | /// the end of a valid entry, to the start of the next valid entry, with | 
|---|
| 12 | /// the invalid syntax in the middle. | 
|---|
| 13 | /// | 
|---|
| 14 | /// | 
|---|
| 15 | /// # Example | 
|---|
| 16 | /// | 
|---|
| 17 | /// ``` | 
|---|
| 18 | /// use fluent_syntax::parser; | 
|---|
| 19 | /// use fluent_syntax::ast; | 
|---|
| 20 | /// | 
|---|
| 21 | /// let ftl = r#" | 
|---|
| 22 | /// key1 = Value 1 | 
|---|
| 23 | /// | 
|---|
| 24 | /// g@Rb@ge = #2y ds | 
|---|
| 25 | /// | 
|---|
| 26 | /// key2 = Value 2 | 
|---|
| 27 | /// | 
|---|
| 28 | /// "#; | 
|---|
| 29 | /// | 
|---|
| 30 | /// let (resource, errors) = parser::parse_runtime(ftl) | 
|---|
| 31 | ///     .expect_err( "Resource should contain errors."); | 
|---|
| 32 | /// | 
|---|
| 33 | /// assert_eq!( | 
|---|
| 34 | ///     errors, | 
|---|
| 35 | ///     vec![ | 
|---|
| 36 | ///         parser::ParserError { | 
|---|
| 37 | ///             pos: 18..19, | 
|---|
| 38 | ///             slice: Some(17..35), | 
|---|
| 39 | ///             kind: parser::ErrorKind::ExpectedToken( '=') | 
|---|
| 40 | ///         } | 
|---|
| 41 | ///     ] | 
|---|
| 42 | /// ); | 
|---|
| 43 | /// | 
|---|
| 44 | /// assert_eq!( | 
|---|
| 45 | ///     resource.body[0], | 
|---|
| 46 | ///     ast::Entry::Message( | 
|---|
| 47 | ///         ast::Message { | 
|---|
| 48 | ///             id: ast::Identifier { | 
|---|
| 49 | ///                 name: "key1" | 
|---|
| 50 | ///             }, | 
|---|
| 51 | ///             value: Some(ast::Pattern { | 
|---|
| 52 | ///                 elements: vec![ | 
|---|
| 53 | ///                     ast::PatternElement::TextElement { | 
|---|
| 54 | ///                         value: "Value 1" | 
|---|
| 55 | ///                     }, | 
|---|
| 56 | ///                 ] | 
|---|
| 57 | ///             }), | 
|---|
| 58 | ///             attributes: vec![], | 
|---|
| 59 | ///             comment: None, | 
|---|
| 60 | ///         } | 
|---|
| 61 | ///     ), | 
|---|
| 62 | /// ); | 
|---|
| 63 | /// | 
|---|
| 64 | /// assert_eq!( | 
|---|
| 65 | ///     resource.body[1], | 
|---|
| 66 | ///     ast::Entry::Junk { | 
|---|
| 67 | ///         content: "g@Rb@ge = #2y ds\n\n " | 
|---|
| 68 | ///     } | 
|---|
| 69 | /// ); | 
|---|
| 70 | /// | 
|---|
| 71 | /// assert_eq!( | 
|---|
| 72 | ///     resource.body[2], | 
|---|
| 73 | ///     ast::Entry::Message( | 
|---|
| 74 | ///         ast::Message { | 
|---|
| 75 | ///             id: ast::Identifier { | 
|---|
| 76 | ///                 name: "key2" | 
|---|
| 77 | ///             }, | 
|---|
| 78 | ///             value: Some(ast::Pattern { | 
|---|
| 79 | ///                 elements: vec![ | 
|---|
| 80 | ///                     ast::PatternElement::TextElement { | 
|---|
| 81 | ///                         value: "Value 2" | 
|---|
| 82 | ///                     }, | 
|---|
| 83 | ///                 ] | 
|---|
| 84 | ///             }), | 
|---|
| 85 | ///             attributes: vec![], | 
|---|
| 86 | ///             comment: None, | 
|---|
| 87 | ///         } | 
|---|
| 88 | ///     ), | 
|---|
| 89 | /// ); | 
|---|
| 90 | /// ``` | 
|---|
| 91 | /// | 
|---|
| 92 | /// The information contained in the `ParserError` should allow the tooling | 
|---|
| 93 | /// to display rich contextual annotations of the error slice, using | 
|---|
| 94 | /// crates such as `annotate-snippers`. | 
|---|
| 95 | #[ derive(Error, Debug, PartialEq, Eq, Clone)] | 
|---|
| 96 | #[error( "{}", self.kind)] | 
|---|
| 97 | pub struct ParserError { | 
|---|
| 98 | /// Precise location of where the parser encountered the error. | 
|---|
| 99 | pub pos: Range<usize>, | 
|---|
| 100 | /// Slice of the input from the end of the last valid entry to the beginning | 
|---|
| 101 | /// of the next valid entry with the invalid syntax in the middle. | 
|---|
| 102 | pub slice: Option<Range<usize>>, | 
|---|
| 103 | /// The type of the error that the parser encountered. | 
|---|
| 104 | pub kind: ErrorKind, | 
|---|
| 105 | } | 
|---|
| 106 |  | 
|---|
| 107 | macro_rules! error { | 
|---|
| 108 | ($kind:expr, $start:expr) => {{ | 
|---|
| 109 | Err(ParserError { | 
|---|
| 110 | pos: $start..$start + 1, | 
|---|
| 111 | slice: None, | 
|---|
| 112 | kind: $kind, | 
|---|
| 113 | }) | 
|---|
| 114 | }}; | 
|---|
| 115 | ($kind:expr, $start:expr, $end:expr) => {{ | 
|---|
| 116 | Err(ParserError { | 
|---|
| 117 | pos: $start..$end, | 
|---|
| 118 | slice: None, | 
|---|
| 119 | kind: $kind, | 
|---|
| 120 | }) | 
|---|
| 121 | }}; | 
|---|
| 122 | } | 
|---|
| 123 |  | 
|---|
| 124 | /// Kind of an error associated with the [`ParserError`]. | 
|---|
| 125 | #[ derive(Error, Debug, PartialEq, Eq, Clone)] | 
|---|
| 126 | pub enum ErrorKind { | 
|---|
| 127 | #[error( "Expected a token starting with \" {0}\" ")] | 
|---|
| 128 | ExpectedToken(char), | 
|---|
| 129 | #[error( "Expected one of \" {range}\" ")] | 
|---|
| 130 | ExpectedCharRange { range: String }, | 
|---|
| 131 | #[error( "Expected a message field for \" {entry_id}\" ")] | 
|---|
| 132 | ExpectedMessageField { entry_id: String }, | 
|---|
| 133 | #[error( "Expected a term field for \" {entry_id}\" ")] | 
|---|
| 134 | ExpectedTermField { entry_id: String }, | 
|---|
| 135 | #[error( "Callee is not allowed here")] | 
|---|
| 136 | ForbiddenCallee, | 
|---|
| 137 | #[error( "The select expression must have a default variant")] | 
|---|
| 138 | MissingDefaultVariant, | 
|---|
| 139 | #[error( "Expected a value")] | 
|---|
| 140 | MissingValue, | 
|---|
| 141 | #[error( "A select expression can only have one default variant")] | 
|---|
| 142 | MultipleDefaultVariants, | 
|---|
| 143 | #[error( "Message references can't be used as a selector")] | 
|---|
| 144 | MessageReferenceAsSelector, | 
|---|
| 145 | #[error( "Term references can't be used as a selector")] | 
|---|
| 146 | TermReferenceAsSelector, | 
|---|
| 147 | #[error( "Message attributes can't be used as a selector")] | 
|---|
| 148 | MessageAttributeAsSelector, | 
|---|
| 149 | #[error( "Term attributes can't be used as a selector")] | 
|---|
| 150 | TermAttributeAsPlaceable, | 
|---|
| 151 | #[error( "Unterminated string literal")] | 
|---|
| 152 | UnterminatedStringLiteral, | 
|---|
| 153 | #[error( "Positional arguments must come before named arguments")] | 
|---|
| 154 | PositionalArgumentFollowsNamed, | 
|---|
| 155 | #[error( "The \" {0}\"  argument appears twice")] | 
|---|
| 156 | DuplicatedNamedArgument(String), | 
|---|
| 157 | #[error( "Unknown escape sequence")] | 
|---|
| 158 | UnknownEscapeSequence(String), | 
|---|
| 159 | #[error( "Invalid unicode escape sequence, \" {0}\" ")] | 
|---|
| 160 | InvalidUnicodeEscapeSequence(String), | 
|---|
| 161 | #[error( "Unbalanced closing brace")] | 
|---|
| 162 | UnbalancedClosingBrace, | 
|---|
| 163 | #[error( "Expected an inline expression")] | 
|---|
| 164 | ExpectedInlineExpression, | 
|---|
| 165 | #[error( "Expected a simple expression as selector")] | 
|---|
| 166 | ExpectedSimpleExpressionAsSelector, | 
|---|
| 167 | #[error( "Expected a string or number literal")] | 
|---|
| 168 | ExpectedLiteral, | 
|---|
| 169 | } | 
|---|
| 170 |  | 
|---|