css_parse/lib.rs
1//! An implementation of [CSS Syntax Level 3][1], plus various additional traits and macros to assist in parsing. It is
2//! intended to be used to build CSS or CSS-alike languages (for example SASS), but isn't able to parse the full CSS
3//! grammar itself. It relies on the foundational [css_lexer] crate.
4//!
5//! This crate provides the [Parser] struct, which builds upon [Lexer][css_lexer::Lexer]. It borrows a `&str` which it
6//! will parse to produce AST nodes (any type that implements the [Parse] and [ToCursors] traits). AST nodes should
7//! parse themselves and any children using [recursive descent][2].
8//!
9//! [1]: https://drafts.csswg.org/css-syntax-3/
10//! [2]: https://en.wikipedia.org/wiki/Recursive_descent_parser
11//!
12//! Parsing requires a heap allocator to allocate into, [Arena] being the allocator of choice. This needs to be
13//! created before parsing, the parser result will have a lifetime bound to the allocator.
14//!
15//! The [Parser] _may_ be configured with additional [Features][Feature] to allow for different parsing or lexing
16//! styles. All features supported by the [Lexer][css_lexer::Lexer] are supported in the [Parser] also (for example
17//! enabling [Feature::SingleLineComments] will enable [the css_lexer feature of the same
18//! name][css_lexer::Feature::SingleLineComments]).
19//!
20//! This crate provides some low level AST nodes that are likely to be common in any CSS-alike language, including the
21//! various base tokens (such as dimensions, and operators). These can be referred to via the [T!] macro, and each [T!]
22//! implements the necessary traits to be parsed as an AST node. For example [T![DashedIdent]][token_macros::DashedIdent]
23//! represents a CSS ident with two leading dashes, and can be parsed and decomposted into its constituent
24//! [Token] (or [Cursor] or [Span]).
25//!
26//! Additionally some generic structs are available to implement the general-purpose parts of [CSS Syntax][1], such as
27//! [ComponentValues]. More on that below in the section titled
28//! [Generic AST Nodes](#generic-ast-nodes).
29//!
30//! Lastly, traits and macros are provided to implement various parsing algorithms to make common parsing operations
31//! easier, for example the [ranged_feature] macro makes it easy to build a node that implements the [RangedFeature]
32//! trait, a trait that provides [an algorithm for parsing a media feature in a range context][3].
33//!
34//! [3]: https://drafts.csswg.org/mediaqueries/#range-context
35//!
36//! Downstream implementations will likely want to build their own AST nodes to represent specific cover grammars, for
37//! example implementing the `@property` rule or the `width:` property declaration. Here's a small guide on what is
38//! required to build such nodes:
39//!
40//! # AST Nodes
41//!
42//! To use this as a library a set of AST nodes will need to be created, the root node (and ideally all nodes) need to
43//! implement [Parse] - which will be given a mutable reference to an active [Parser]. Each Node will likely be a
44//! collection of other Nodes, calling [Parser::parse<T>()][Parser::parse] (where `T` is each child Node). Leaf Nodes will likely be
45//! wrappers around a single token (tip: use the [T!] nodes which cover all single token needs):
46//!
47//! ```
48//! use css_parse::*;
49//! struct MyProperty {
50//! ident: T![Ident],
51//! colon: T![Colon],
52//! dimension: T![Dimension],
53//! }
54//! impl<'a> Parse<'a> for MyProperty {
55//! fn parse<I>(p: &mut Parser<'a, I>) -> Result<Self>
56//! where
57//! I: Iterator<Item = Cursor> + Clone,
58//! {
59//! let ident = p.parse::<T![Ident]>()?;
60//! let colon = p.parse::<T![Colon]>()?;
61//! let dimension = p.parse::<T![Dimension]>()?;
62//! Ok(Self { ident, colon, dimension })
63//! }
64//! }
65//! ```
66//!
67//! AST nodes will also need to implement [ToCursors] - which is given an abstract [CursorSink] to put the cursors back
68//! into, in order, so that they can be built back up into the original source text. Implementing [ToCursors] allows
69//! for all manner of other useful downstream operations such as concatenation, transforms (e.g. minification) and so
70//! on.
71//!
72//! ```
73//! use css_parse::*;
74//! struct MyProperty {
75//! ident: T![Ident],
76//! colon: T![Colon],
77//! dimension: T![Dimension],
78//! }
79//! impl ToCursors for MyProperty {
80//! fn to_cursors(&self, s: &mut impl CursorSink) {
81//! s.append(self.ident.into());
82//! s.append(self.colon.into());
83//! s.append(self.dimension.into());
84//! }
85//! }
86//! ```
87//!
88//! Both [Parse] and [ToCursors] are the _required_ trait implemenetations, but several more are also available and make
89//! the work of Parsing (or downstream analysis) easier...
90//!
91//! ## Peekable nodes
92//!
93//! Everything that implements [Parse] is required to implement [Parse::parse()], but gets [Parse::try_parse()] for
94//! free, which allows parent nodes to more easily branch by parsing a node, resetting during failure.
95//! [Parse::try_parse()] can be expensive though - parsing a Node is pretty much guaranteed to advance the [Parser]
96//! some number of tokens forward, and so a parser checkpoint needs to be stored so that - should
97//! [Parse::parse()] fail - the [Parser] can be rewound to that checkpoint as if the operation never happened. Reading
98//! N tokens forward only to forget that and re-do it all over can be costly and is likely the _wrong tool_ to use when
99//! faced with a set of branching Nodes with an ambiguity of which to parse. So Nodes are also encouraged to implement
100//! [Peek], which their parent nodes can call to check as an indicator that this Node may viably parse.
101//!
102//! Most nodes will know they can only accept a certain number of tokens, per their cover grammar. [Peek] is a useful
103//! way to encode this; [Peek::peek] gets an _immutable_ reference to the [Parser], from which it can call
104//! [Parser::peek_n()] (an immutable operation that can't change the position of the parser) to look ahead to other
105//! tokens and establish if they would cause [Parse::parse()] to fail. There is still a cost to this, and so
106//! [Peek::peek] should only look ahead the smallest number of tokens to confidently know that it can begin parsing,
107//! rather than looking ahead a large number of tokens. For the most part peeking 1 or two tokens should be sufficient.
108//! An easy implementation for [Peek] is to simply set the [Peek::PEEK_KINDSET] const, which the provided
109//! implementation of [Peek::peek()] will use to check the cursor matches this [KindSet].
110//!
111//! ```
112//! use css_parse::*;
113//! use {Kind, KindSet};
114//! enum LengthOrAuto {
115//! Length(T![Dimension]), // A Dimension, like `px`
116//! Auto(T![Ident]), // The Ident of `auto`
117//! }
118//! impl<'a> Peek<'a> for LengthOrAuto {
119//! const PEEK_KINDSET: KindSet = KindSet::new(&[Kind::Dimension, Kind::Ident]);
120//! }
121//! ```
122//!
123//! ## Single token Nodes
124//!
125//! If a node represents just a single token, for example a keyword, then its [Parse] implementation
126//! should call [Parser::peek] to check if it can be parsed, then [Parser::next] to get the cursor, and construct
127//! the node from that cursor. The [Peek] trait should accurately determine if the Node can be parsed from the
128//! given [Cursor]. Single token parsing may need to branch if it is an enum of variants:
129//!
130//! ```
131//! use css_parse::*;
132//! enum LengthOrAuto {
133//! Length(T![Dimension]), // A Dimension, like `px`
134//! Auto(T![Ident]), // The Ident of `auto`
135//! }
136//! impl<'a> Peek<'a> for LengthOrAuto {
137//! const PEEK_KINDSET: KindSet = KindSet::new(&[Kind::Dimension, Kind::Ident]);
138//! }
139//! impl<'a> Parse<'a> for LengthOrAuto {
140//! fn parse<I>(p: &mut Parser<'a, I>) -> Result<Self>
141//! where
142//! I: Iterator<Item = Cursor> + Clone,
143//! {
144//! if p.peek::<T![Dimension]>() {
145//! p.parse::<T![Dimension]>().map(Self::Length)
146//! } else {
147//! p.parse::<T![Ident]>().map(Self::Auto)
148//! }
149//! }
150//! }
151//! ```
152//!
153//! ## Convenience algorithms
154//!
155//! For more complex algorithms where nodes might parse many child nodes or have some delicate or otherwise awkward
156//! steps, additional traits exist to make implementing AST nodes trivial for these use cases.
157//!
158//! - [StyleSheet] - AST nodes representing a stylesheet should use this to, well, [parse a stylesheet][4].
159//! - [Declaration] - AST nodes representing a declaration (aka "property") should use this to [parse a
160//! declaration][5].
161//! - [QualifiedRule] - AST nodes representing a "Qualified Rule" (e.g. a style rule) should use this to
162//! [parse a QualifiedRule][7].
163//! - [CompoundSelector] - AST nodes representing a CSS selector should use this to parse a list of nodes implementing
164//! [SelectorComponent].
165//! - [SelectorComponent] - AST nodes representing an individual selector component, such as a tag or class or pseudo
166//! element, should use this to parse the set of specified selector components.
167//!
168//! The `*List` traits are also available to more easily parse lists of things, such as preludes or blocks:
169//!
170//! - [FeatureConditionList] - AST nodes representing a prelude "condition list" should use this. It parses the complex
171//! condition logic in rules like `@media`, `@supports` or `@container`.
172//! - [DeclarationList] - AST nodes representing a block which can only accept "Declarations" should use this. This is
173//! an implementation of [`<declaration-list>`][8].
174//! - [RuleList] - AST nodes representing a block which can accept either "At Rules" or "Qualfiied Rules" but cannot
175//! accept "Declarations" should use this. This is an implementation of [`<rule-list>`][12].
176//!
177//! The `*Feature` traits are also available to more easily parse "features conditions", these are the conditions
178//! supports in a [FeatureConditionList], e.g. the conditions inside of `@media`, `@container` or `@supports` rules.
179//!
180//! - [RangedFeature] - AST nodes representing a feature condition in the "ranged" context.
181//! - [BooleanFeature] - AST nodes representing a feature condition in the "boolean" context.
182//! - [DiscreteFeature] - AST nodes representing a feature condition with discrete keywords.
183//!
184//! [4]: https://drafts.csswg.org/css-syntax-3/#consume-stylesheet-contents
185//! [5]: https://drafts.csswg.org/css-syntax-3/#consume-declaration
186//! [6]: https://drafts.csswg.org/css-syntax-3/#consume-at-rule
187//! [7]: https://drafts.csswg.org/css-syntax-3/#consume-qualified-rule
188//! [8]: https://drafts.csswg.org/css-syntax-3/#typedef-declaration-list
189//! [9]: https://drafts.csswg.org/css-syntax-3/#typedef-qualified-rule-list
190//! [10]: https://drafts.csswg.org/css-syntax-3/#typedef-at-rule-list
191//! [11]: https://drafts.csswg.org/css-syntax-3/#typedef-declaration-rule-list
192//! [12]: https://drafts.csswg.org/css-syntax-3/#typedef-rule-list
193//!
194//! # Generic AST nodes
195//!
196//! In addition to the traits which allow for parsing bespoke AST Nodes, this crate provides a set of generic AST node
197//! structs/enums which are capable of providing "general purpose" AST nodes, useful for when an AST node fails to parse
198//! and needs to consume some tokens in a generic manner, according to the rules of :
199//!
200//! - [syntax::QualifiedRule] provides the generic [`<qualified-rule>` grammar][14].
201//! - [syntax::Declaration] provides the generic [`<declaration>` grammar][15].
202//! - [syntax::BangImportant] provides the [`<!important>` grammar][16].
203//! - [syntax::ComponentValue] provides the [`<component-value>` grammar][17], used by other generic nodes.
204//! - [syntax::SimpleBlock] provides the generic [`<simple-block>` grammar][18].
205//! - [syntax::FunctionBlock] provides the generic [`<function-block>` grammar][19].
206//! - [syntax::ComponentValues] provides a list of `<component-value>` nodes, [per "parse a list of component
207//! values"][20].
208//! - [syntax::BadDeclaration] provides a struct to capture the [bad declaration steps][21].
209//!
210//! [13]: https://drafts.csswg.org/css-syntax-3/#at-rule-diagram
211//! [14]: https://drafts.csswg.org/css-syntax-3/#qualified-rule-diagram
212//! [15]: https://drafts.csswg.org/css-syntax-3/#declaration-diagram
213//! [16]: https://drafts.csswg.org/css-syntax-3/#!important-diagram
214//! [17]: https://drafts.csswg.org/css-syntax-3/#component-value-diagram
215//! [18]: https://drafts.csswg.org/css-syntax-3/#simple-block-diagram
216//! [19]: https://drafts.csswg.org/css-syntax-3/#function-block-diagram
217//! [20]: https://drafts.csswg.org/css-syntax-3/#parse-list-of-component-values
218//! [21]: https://drafts.csswg.org/css-syntax-3/#consume-the-remnants-of-a-bad-declaration
219//!
220//! # Test Helpers
221//!
222//! In order to make it much easier to test the functionality of AST nodes, enabling the `testing` feature will provide
223//! two testing macros which make setting up a test trivial.
224//!
225//! - [assert_parse!] will parse the given string against the given node, asserting that it parses successfully and can
226//! be written back out to the same output.
227//!
228//! - [assert_parse_error!] will parse the given string against the node, expecting the parse to fail.
229//!
230//! It is advised to add the `testing` flag as a `dev-dependencies` feature to enable these only during test:
231//!
232//! ```toml
233//! [dependencies]
234//! css_parse = "*"
235//!
236//! [dev-dependencies]
237//! css_parse = { version = "*", features = ["testing"] }
238//! ```
239//!
240//! # Example
241//!
242//! A small example on how to define an AST node:
243//!
244//! ```
245//! use css_parse::*;
246//! #[derive(Debug)]
247//! struct MyProperty {
248//! ident: T![Ident],
249//! colon: T![Colon],
250//! dimension: T![Dimension],
251//! }
252//!
253//! impl<'a> Peek<'a> for MyProperty {
254//! const PEEK_KINDSET: KindSet = KindSet::new(&[Kind::Ident]);
255//! }
256//!
257//! impl<'a> Parse<'a> for MyProperty {
258//! fn parse<I>(p: &mut Parser<'a, I>) -> Result<Self>
259//! where
260//! I: Iterator<Item = Cursor> + Clone,
261//! {
262//! let ident = p.parse::<T![Ident]>()?;
263//! let colon = p.parse::<T![Colon]>()?;
264//! let dimension = p.parse::<T![Dimension]>()?;
265//! Ok(Self { ident, colon, dimension })
266//! }
267//! }
268//! impl ToCursors for MyProperty {
269//! fn to_cursors(&self, s: &mut impl CursorSink) {
270//! self.ident.to_cursors(s);
271//! self.colon.to_cursors(s);
272//! self.dimension.to_cursors(s);
273//! }
274//! }
275//!
276//! assert_parse!(EmptyAtomSet::ATOMS, MyProperty, "width:1px");
277//! ```
278
279// Re-export commonly used components from css_lexer:
280pub use atom_set::AtomSet;
281pub use css_lexer::{
282 AssociatedWhitespaceRules, Cursor, EmptyAtomSet, Kind, KindSet, PairWise, QuoteStyle, SourceCursor, SourceOffset,
283 Span, ToSpan, Token, Whitespace,
284};
285
286mod arena_impls;
287mod comparison;
288mod cursor_compact_write_sink;
289#[cfg(feature = "egg")]
290mod cursor_expanded_write_sink;
291mod cursor_interleave_sink;
292mod cursor_ordered_sink;
293mod cursor_overlay_sink;
294mod cursor_pretty_write_sink;
295mod cursor_to_source_cursor_sink;
296mod cursor_write_sink;
297mod diagnostics;
298mod feature;
299#[cfg(test)]
300mod layout_test;
301mod macros;
302mod parser;
303mod parser_checkpoint;
304mod parser_return;
305/// Various structs/enums that represent generic AST nodes.
306pub mod syntax;
307/// Test macros available if built with `features = ["testing"]`
308#[cfg(any(feature = "testing", test))]
309pub mod test_helpers;
310/// Various macros that expand to AST nodes that wrap [Tokens][Token].
311pub mod token_macros;
312mod traits;
313
314pub type Result<T> = std::result::Result<T, diagnostics::Diagnostic>;
315
316pub type Arena = csskit_arena::Arena;
317
318pub use comparison::*;
319pub use csskit_arena::{Box, Drain, IntoIter, String, Vec, format_in, vec_in};
320pub use cursor_compact_write_sink::*;
321#[cfg(feature = "egg")]
322pub use cursor_expanded_write_sink::*;
323pub use cursor_interleave_sink::*;
324pub use cursor_ordered_sink::*;
325pub use cursor_overlay_sink::*;
326pub use cursor_pretty_write_sink::*;
327pub use cursor_to_source_cursor_sink::*;
328pub use cursor_write_sink::*;
329pub use diagnostics::*;
330pub use feature::*;
331pub use macros::optionals::*;
332#[cfg(feature = "miette")]
333pub use miette::Error;
334pub use parser::*;
335pub use parser_checkpoint::*;
336pub use parser_return::*;
337pub use syntax::*;
338pub use traits::*;