1 | //! Fluent Translation List parsing utilities |
2 | //! |
3 | //! FTL resources can be parsed using one of two methods: |
4 | //! * [`parse`] - parses an input into a complete Abstract Syntax Tree representation with all source information preserved. |
5 | //! * [`parse_runtime`] - parses an input into a runtime optimized Abstract Syntax Tree |
6 | //! representation with comments stripped. |
7 | //! |
8 | //! # Example |
9 | //! |
10 | //! ``` |
11 | //! use fluent_syntax::parser; |
12 | //! use fluent_syntax::ast; |
13 | //! |
14 | //! let ftl = r#" |
15 | //! #### Resource Level Comment |
16 | //! |
17 | //! ## This is a message comment |
18 | //! hello-world = Hello World! |
19 | //! |
20 | //! "# ; |
21 | //! |
22 | //! let resource = parser::parse(ftl) |
23 | //! .expect("Failed to parse an FTL resource." ); |
24 | //! |
25 | //! assert_eq!( |
26 | //! resource.body[0], |
27 | //! ast::Entry::ResourceComment( |
28 | //! ast::Comment { |
29 | //! content: vec![ |
30 | //! "Resource Level Comment" |
31 | //! ] |
32 | //! } |
33 | //! ) |
34 | //! ); |
35 | //! assert_eq!( |
36 | //! resource.body[1], |
37 | //! ast::Entry::Message( |
38 | //! ast::Message { |
39 | //! id: ast::Identifier { |
40 | //! name: "hello-world" |
41 | //! }, |
42 | //! value: Some(ast::Pattern { |
43 | //! elements: vec![ |
44 | //! ast::PatternElement::TextElement { |
45 | //! value: "Hello World!" |
46 | //! }, |
47 | //! ] |
48 | //! }), |
49 | //! attributes: vec![], |
50 | //! comment: Some( |
51 | //! ast::Comment { |
52 | //! content: vec!["This is a message comment" ] |
53 | //! } |
54 | //! ) |
55 | //! } |
56 | //! ), |
57 | //! ); |
58 | //! ``` |
59 | //! |
60 | //! # Error Recovery |
61 | //! |
62 | //! In both modes the parser is lenient, attempting to recover from errors. |
63 | //! |
64 | //! The [`Result`] return the resulting AST in both scenarios, and in the |
65 | //! error scenario a vector of [`ParserError`] elements is returned as well. |
66 | //! |
67 | //! Any unparsed parts of the input are returned as [`ast::Entry::Junk`] elements. |
68 | #[macro_use ] |
69 | mod errors; |
70 | #[macro_use ] |
71 | mod macros; |
72 | mod comment; |
73 | mod core; |
74 | mod expression; |
75 | mod helper; |
76 | mod pattern; |
77 | mod runtime; |
78 | mod slice; |
79 | |
80 | use crate::ast; |
81 | pub use errors::{ErrorKind, ParserError}; |
82 | pub use slice::Slice; |
83 | |
84 | /// Parser result always returns an AST representation of the input, |
85 | /// and if parsing errors were encountered, a list of [`ParserError`] elements |
86 | /// is also returned. |
87 | /// |
88 | /// # Example |
89 | /// |
90 | /// ``` |
91 | /// use fluent_syntax::parser; |
92 | /// use fluent_syntax::ast; |
93 | /// |
94 | /// let ftl = r#" |
95 | /// key1 = Value 1 |
96 | /// |
97 | /// g@Rb@ge = #2y ds |
98 | /// |
99 | /// key2 = Value 2 |
100 | /// |
101 | /// "# ; |
102 | /// |
103 | /// let (resource, errors) = parser::parse_runtime(ftl) |
104 | /// .expect_err("Resource should contain errors." ); |
105 | /// |
106 | /// assert_eq!( |
107 | /// errors, |
108 | /// vec![ |
109 | /// parser::ParserError { |
110 | /// pos: 18..19, |
111 | /// slice: Some(17..35), |
112 | /// kind: parser::ErrorKind::ExpectedToken('=' ) |
113 | /// } |
114 | /// ] |
115 | /// ); |
116 | /// |
117 | /// assert_eq!( |
118 | /// resource.body[0], |
119 | /// ast::Entry::Message( |
120 | /// ast::Message { |
121 | /// id: ast::Identifier { |
122 | /// name: "key1" |
123 | /// }, |
124 | /// value: Some(ast::Pattern { |
125 | /// elements: vec![ |
126 | /// ast::PatternElement::TextElement { |
127 | /// value: "Value 1" |
128 | /// }, |
129 | /// ] |
130 | /// }), |
131 | /// attributes: vec![], |
132 | /// comment: None, |
133 | /// } |
134 | /// ), |
135 | /// ); |
136 | /// |
137 | /// assert_eq!( |
138 | /// resource.body[1], |
139 | /// ast::Entry::Junk { |
140 | /// content: "g@Rb@ge = #2y ds \n\n" |
141 | /// } |
142 | /// ); |
143 | /// |
144 | /// assert_eq!( |
145 | /// resource.body[2], |
146 | /// ast::Entry::Message( |
147 | /// ast::Message { |
148 | /// id: ast::Identifier { |
149 | /// name: "key2" |
150 | /// }, |
151 | /// value: Some(ast::Pattern { |
152 | /// elements: vec![ |
153 | /// ast::PatternElement::TextElement { |
154 | /// value: "Value 2" |
155 | /// }, |
156 | /// ] |
157 | /// }), |
158 | /// attributes: vec![], |
159 | /// comment: None, |
160 | /// } |
161 | /// ), |
162 | /// ); |
163 | /// ``` |
164 | pub type Result<S> = std::result::Result<ast::Resource<S>, (ast::Resource<S>, Vec<ParserError>)>; |
165 | |
166 | /// Parses an input into a complete Abstract Syntax Tree representation with |
167 | /// all source information preserved. |
168 | /// |
169 | /// This mode is intended for tooling, linters and other scenarios where |
170 | /// complete representation, with comments, is preferred over speed and memory |
171 | /// utilization. |
172 | /// |
173 | /// # Example |
174 | /// |
175 | /// ``` |
176 | /// use fluent_syntax::parser; |
177 | /// use fluent_syntax::ast; |
178 | /// |
179 | /// let ftl = r#" |
180 | /// #### Resource Level Comment |
181 | /// |
182 | /// ## This is a message comment |
183 | /// hello-world = Hello World! |
184 | /// |
185 | /// "# ; |
186 | /// |
187 | /// let resource = parser::parse(ftl) |
188 | /// .expect("Failed to parse an FTL resource." ); |
189 | /// |
190 | /// assert_eq!( |
191 | /// resource.body[0], |
192 | /// ast::Entry::ResourceComment( |
193 | /// ast::Comment { |
194 | /// content: vec![ |
195 | /// "Resource Level Comment" |
196 | /// ] |
197 | /// } |
198 | /// ) |
199 | /// ); |
200 | /// assert_eq!( |
201 | /// resource.body[1], |
202 | /// ast::Entry::Message( |
203 | /// ast::Message { |
204 | /// id: ast::Identifier { |
205 | /// name: "hello-world" |
206 | /// }, |
207 | /// value: Some(ast::Pattern { |
208 | /// elements: vec![ |
209 | /// ast::PatternElement::TextElement { |
210 | /// value: "Hello World!" |
211 | /// }, |
212 | /// ] |
213 | /// }), |
214 | /// attributes: vec![], |
215 | /// comment: Some( |
216 | /// ast::Comment { |
217 | /// content: vec!["This is a message comment" ] |
218 | /// } |
219 | /// ) |
220 | /// } |
221 | /// ), |
222 | /// ); |
223 | /// ``` |
224 | pub fn parse<'s, S>(input: S) -> Result<S> |
225 | where |
226 | S: Slice<'s>, |
227 | { |
228 | core::Parser::new(source:input).parse() |
229 | } |
230 | |
231 | /// Parses an input into an Abstract Syntax Tree representation with comments stripped. |
232 | /// |
233 | /// This mode is intended for runtime use of Fluent. It currently strips all |
234 | /// comments improving parsing performance and reducing the size of the AST tree. |
235 | /// |
236 | /// # Example |
237 | /// |
238 | /// ``` |
239 | /// use fluent_syntax::parser; |
240 | /// use fluent_syntax::ast; |
241 | /// |
242 | /// let ftl = r#" |
243 | /// #### Resource Level Comment |
244 | /// |
245 | /// ## This is a message comment |
246 | /// hello-world = Hello World! |
247 | /// |
248 | /// "# ; |
249 | /// |
250 | /// let resource = parser::parse_runtime(ftl) |
251 | /// .expect("Failed to parse an FTL resource." ); |
252 | /// |
253 | /// assert_eq!( |
254 | /// resource.body[0], |
255 | /// ast::Entry::Message( |
256 | /// ast::Message { |
257 | /// id: ast::Identifier { |
258 | /// name: "hello-world" |
259 | /// }, |
260 | /// value: Some(ast::Pattern { |
261 | /// elements: vec![ |
262 | /// ast::PatternElement::TextElement { |
263 | /// value: "Hello World!" |
264 | /// }, |
265 | /// ] |
266 | /// }), |
267 | /// attributes: vec![], |
268 | /// comment: None, |
269 | /// } |
270 | /// ), |
271 | /// ); |
272 | /// ``` |
273 | pub fn parse_runtime<'s, S>(input: S) -> Result<S> |
274 | where |
275 | S: Slice<'s>, |
276 | { |
277 | core::Parser::new(source:input).parse_runtime() |
278 | } |
279 | |