fluent_syntax/parser/
mod.rs

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]
69mod errors;
70#[macro_use]
71mod macros;
72mod comment;
73mod core;
74mod expression;
75mod helper;
76mod pattern;
77mod runtime;
78mod slice;
79
80use crate::ast;
81pub use errors::{ErrorKind, ParserError};
82pub(crate) use slice::matches_fluent_ws;
83pub 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/// ```
165pub 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/// ```
225pub fn parse<'s, S>(input: S) -> Result<S>
226where
227    S: Slice<'s>,
228{
229    core::Parser::new(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/// ```
274pub fn parse_runtime<'s, S>(input: S) -> Result<S>
275where
276    S: Slice<'s>,
277{
278    core::Parser::new(input).parse_runtime()
279}