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(crate) use slice::matches_fluent_ws; |
83 | pub use slice::Slice; |
84 | |
85 | /// Parser result always returns an AST representation of the input, |
86 | /// and if parsing errors were encountered, a list of [`ParserError`] elements |
87 | /// is also returned. |
88 | /// |
89 | /// # Example |
90 | /// |
91 | /// ``` |
92 | /// use fluent_syntax::parser; |
93 | /// use fluent_syntax::ast; |
94 | /// |
95 | /// let ftl = r#" |
96 | /// key1 = Value 1 |
97 | /// |
98 | /// g@Rb@ge = #2y ds |
99 | /// |
100 | /// key2 = Value 2 |
101 | /// |
102 | /// "# ; |
103 | /// |
104 | /// let (resource, errors) = parser::parse_runtime(ftl) |
105 | /// .expect_err("Resource should contain errors." ); |
106 | /// |
107 | /// assert_eq!( |
108 | /// errors, |
109 | /// vec![ |
110 | /// parser::ParserError { |
111 | /// pos: 18..19, |
112 | /// slice: Some(17..35), |
113 | /// kind: parser::ErrorKind::ExpectedToken('=' ) |
114 | /// } |
115 | /// ] |
116 | /// ); |
117 | /// |
118 | /// assert_eq!( |
119 | /// resource.body[0], |
120 | /// ast::Entry::Message( |
121 | /// ast::Message { |
122 | /// id: ast::Identifier { |
123 | /// name: "key1" |
124 | /// }, |
125 | /// value: Some(ast::Pattern { |
126 | /// elements: vec![ |
127 | /// ast::PatternElement::TextElement { |
128 | /// value: "Value 1" |
129 | /// }, |
130 | /// ] |
131 | /// }), |
132 | /// attributes: vec![], |
133 | /// comment: None, |
134 | /// } |
135 | /// ), |
136 | /// ); |
137 | /// |
138 | /// assert_eq!( |
139 | /// resource.body[1], |
140 | /// ast::Entry::Junk { |
141 | /// content: "g@Rb@ge = #2y ds \n\n" |
142 | /// } |
143 | /// ); |
144 | /// |
145 | /// assert_eq!( |
146 | /// resource.body[2], |
147 | /// ast::Entry::Message( |
148 | /// ast::Message { |
149 | /// id: ast::Identifier { |
150 | /// name: "key2" |
151 | /// }, |
152 | /// value: Some(ast::Pattern { |
153 | /// elements: vec![ |
154 | /// ast::PatternElement::TextElement { |
155 | /// value: "Value 2" |
156 | /// }, |
157 | /// ] |
158 | /// }), |
159 | /// attributes: vec![], |
160 | /// comment: None, |
161 | /// } |
162 | /// ), |
163 | /// ); |
164 | /// ``` |
165 | pub type Result<S> = std::result::Result<ast::Resource<S>, (ast::Resource<S>, Vec<ParserError>)>; |
166 | |
167 | /// Parses an input into a complete Abstract Syntax Tree representation with |
168 | /// all source information preserved. |
169 | /// |
170 | /// This mode is intended for tooling, linters and other scenarios where |
171 | /// complete representation, with comments, is preferred over speed and memory |
172 | /// utilization. |
173 | /// |
174 | /// # Example |
175 | /// |
176 | /// ``` |
177 | /// use fluent_syntax::parser; |
178 | /// use fluent_syntax::ast; |
179 | /// |
180 | /// let ftl = r#" |
181 | /// #### Resource Level Comment |
182 | /// |
183 | /// ## This is a message comment |
184 | /// hello-world = Hello World! |
185 | /// |
186 | /// "# ; |
187 | /// |
188 | /// let resource = parser::parse(ftl) |
189 | /// .expect("Failed to parse an FTL resource." ); |
190 | /// |
191 | /// assert_eq!( |
192 | /// resource.body[0], |
193 | /// ast::Entry::ResourceComment( |
194 | /// ast::Comment { |
195 | /// content: vec![ |
196 | /// "Resource Level Comment" |
197 | /// ] |
198 | /// } |
199 | /// ) |
200 | /// ); |
201 | /// assert_eq!( |
202 | /// resource.body[1], |
203 | /// ast::Entry::Message( |
204 | /// ast::Message { |
205 | /// id: ast::Identifier { |
206 | /// name: "hello-world" |
207 | /// }, |
208 | /// value: Some(ast::Pattern { |
209 | /// elements: vec![ |
210 | /// ast::PatternElement::TextElement { |
211 | /// value: "Hello World!" |
212 | /// }, |
213 | /// ] |
214 | /// }), |
215 | /// attributes: vec![], |
216 | /// comment: Some( |
217 | /// ast::Comment { |
218 | /// content: vec!["This is a message comment" ] |
219 | /// } |
220 | /// ) |
221 | /// } |
222 | /// ), |
223 | /// ); |
224 | /// ``` |
225 | pub fn parse<'s, S>(input: S) -> Result<S> |
226 | where |
227 | S: Slice<'s>, |
228 | { |
229 | core::Parser::new(source:input).parse() |
230 | } |
231 | |
232 | /// Parses an input into an Abstract Syntax Tree representation with comments stripped. |
233 | /// |
234 | /// This mode is intended for runtime use of Fluent. It currently strips all |
235 | /// comments improving parsing performance and reducing the size of the AST tree. |
236 | /// |
237 | /// # Example |
238 | /// |
239 | /// ``` |
240 | /// use fluent_syntax::parser; |
241 | /// use fluent_syntax::ast; |
242 | /// |
243 | /// let ftl = r#" |
244 | /// #### Resource Level Comment |
245 | /// |
246 | /// ## This is a message comment |
247 | /// hello-world = Hello World! |
248 | /// |
249 | /// "# ; |
250 | /// |
251 | /// let resource = parser::parse_runtime(ftl) |
252 | /// .expect("Failed to parse an FTL resource." ); |
253 | /// |
254 | /// assert_eq!( |
255 | /// resource.body[0], |
256 | /// ast::Entry::Message( |
257 | /// ast::Message { |
258 | /// id: ast::Identifier { |
259 | /// name: "hello-world" |
260 | /// }, |
261 | /// value: Some(ast::Pattern { |
262 | /// elements: vec![ |
263 | /// ast::PatternElement::TextElement { |
264 | /// value: "Hello World!" |
265 | /// }, |
266 | /// ] |
267 | /// }), |
268 | /// attributes: vec![], |
269 | /// comment: None, |
270 | /// } |
271 | /// ), |
272 | /// ); |
273 | /// ``` |
274 | pub fn parse_runtime<'s, S>(input: S) -> Result<S> |
275 | where |
276 | S: Slice<'s>, |
277 | { |
278 | core::Parser::new(source:input).parse_runtime() |
279 | } |
280 | |