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}