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