Skip to content

Commit d788826

Browse files
authored
Extensively document and clean up css_lexer/css_parse (#78)
Yet another giant refactor: - Removed `hdx_atom` crate, instead opting to do interning on each node. This de-couples the lexer & parser from _css_ the language itself. - Fold in `hdx_syntax` into `hdx_lexer` as it wasn't used outside of that. - Rename `hdx_lexer` to `css_lexer`. - Rename `hdx_parser` to `css_parse`. - Extensively document `css_lexer` & `css_parse`. - Fold in a bunch of the macros in `hdx_ast` into `css_parse`. - Rename `hdx_ast` to `css_ast` - make it just for CSS (the original intent was for `hdx_ast` to have multiple language ASTs, e.g. `sass`, but I think now we can add a `sass_ast` crate instead).
1 parent 7642cad commit d788826

File tree

652 files changed

+1667472
-1600639
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

652 files changed

+1667472
-1600639
lines changed

Cargo.lock

+126-171
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+4-5
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,10 @@ repository = "https://github.com/keithamus/hdx"
1515
[workspace.dependencies]
1616
# Local packages
1717
hdx = { version = "0.0.1", path = "crates/hdx" }
18-
hdx_atom = { version = "0.0.0", path = "crates/hdx_atom" }
1918
hdx_proc_macro = { version = "0.0.0", path = "crates/hdx_proc_macro" }
20-
hdx_parser = { version = "0.0.1", path = "crates/hdx_parser" }
21-
hdx_lexer = { version = "0.0.1", path = "crates/hdx_lexer" }
22-
hdx_syntax = { version = "0.0.0", path = "crates/hdx_syntax" }
23-
hdx_ast = { version = "0.0.0", path = "crates/hdx_ast" }
19+
css_parse = { version = "0.0.1", path = "crates/css_parse" }
20+
css_lexer = { version = "0.0.1", path = "crates/css_lexer" }
21+
css_ast = { version = "0.0.0", path = "crates/css_ast" }
2422
hdx_transform = { version = "0.0.0", path = "crates/hdx_transform" }
2523
hdx_highlight = { version = "0.0.0", path = "crates/hdx_highlight" }
2624
hdx_lsp = { version = "0.0.0", path = "crates/hdx_lsp" }
@@ -34,6 +32,7 @@ itertools = { version = "0.13.0" }
3432
ropey = { version = "1.6.1" }
3533
smallvec = { version = "1.13.2" }
3634
strum = { version = "0.26.3" }
35+
phf = { version = "0.11.2" }
3736

3837
# CLI
3938
clap = { version = "4.5.23" }

crates/hdx_ast/Cargo.toml crates/css_ast/Cargo.toml

+12-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[package]
2-
name = "hdx_ast"
2+
name = "css_ast"
33
version = "0.0.0"
44
authors.workspace = true
55
description.workspace = true
@@ -13,14 +13,14 @@ repository.workspace = true
1313
bench = false
1414

1515
[dependencies]
16-
hdx_lexer = { workspace = true }
17-
hdx_atom = { workspace = true }
18-
hdx_parser = { workspace = true }
16+
css_lexer = { workspace = true }
17+
css_parse = { workspace = true }
1918
hdx_proc_macro = { workspace = true }
2019

2120
bumpalo = { workspace = true, features = ["collections", "boxed"] }
2221
miette = { workspace = true, features = ["derive"] }
2322
smallvec = { workspace = true }
23+
phf = { workspace = true, features = ["macros"] }
2424

2525
serde = { workspace = true, optional = true }
2626
serde_json = { workspace = true, optional = true }
@@ -34,6 +34,7 @@ grep-matcher = { workspace = true }
3434
glob = { workspace = true }
3535

3636
[dev-dependencies]
37+
css_parse = { workspace = true, features = ["testing"] }
3738
glob = { workspace = true }
3839
serde = { workspace = true, features = ["derive"] }
3940
serde_json = { workspace = true }
@@ -47,7 +48,13 @@ pprof = { workspace = true, features = ["flamegraph", "criterion"] }
4748

4849
[features]
4950
default = []
50-
serde = ["dep:serde", "dep:serde_json", "hdx_atom/serde", "hdx_parser/serde", "hdx_lexer/serde", "smallvec/serde"]
51+
serde = [
52+
"dep:serde",
53+
"dep:serde_json",
54+
"css_parse/serde",
55+
"css_lexer/serde",
56+
"smallvec/serde",
57+
]
5158
fancy = ["miette/fancy-no-backtrace"]
5259

5360
[[bench]]

crates/hdx_ast/benches/parse_popular.rs crates/css_ast/benches/parse_popular.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use bumpalo::Bump;
22
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
3+
use css_ast::StyleSheet;
4+
use css_parse::Parser;
35
use glob::glob;
4-
use hdx_ast::css::StyleSheet;
5-
use hdx_parser::{Features, Parser};
66
#[cfg(target_family = "unix")]
77
use pprof::criterion::{Output, PProfProfiler};
88
use std::fs::read_to_string;
@@ -32,7 +32,7 @@ fn popular(c: &mut Criterion) {
3232
group.bench_with_input(BenchmarkId::from_parameter(&file.name), &file.source_text, |b, source_text| {
3333
b.iter_with_large_drop(|| {
3434
let allocator = Bump::default();
35-
let _ = Parser::new(&allocator, source_text, Features::default()).parse_entirely::<StyleSheet>();
35+
let _ = Parser::new(&allocator, source_text).parse_entirely::<StyleSheet>();
3636

3737
allocator
3838
});

crates/hdx_ast/build.rs crates/css_ast/build.rs

+45-10
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ pub struct NodeMatcher<'a> {
3636
stylevalue_matches: &'a mut HashSet<String>,
3737
}
3838

39-
impl<'a> Sink for NodeMatcher<'a> {
39+
impl Sink for NodeMatcher<'_> {
4040
type Error = io::Error;
4141

4242
fn matched(&mut self, _searcher: &Searcher, mat: &SinkMatch<'_>) -> Result<bool, io::Error> {
@@ -46,13 +46,25 @@ impl<'a> Sink for NodeMatcher<'a> {
4646
Err(err) => return Err(io::Error::error_message(err)),
4747
};
4848
self.matcher.captures_iter(mat.bytes(), &mut captures, |captures| -> bool {
49+
dbg!(
50+
&line,
51+
&captures,
52+
captures.get(2).map(|r| &line[r]),
53+
captures.get(3).map(|r| &line[r]),
54+
captures.get(4).map(|r| &line[r]),
55+
captures.get(5).map(|r| &line[r]),
56+
captures.get(6).map(|r| &line[r])
57+
);
4958
let value_or_visit = &line[captures.get(1).unwrap()];
50-
let capture = &line[captures.get(2).unwrap()];
59+
let capture = &line[captures.get(6).unwrap()];
5160
if !capture.is_empty() {
5261
if value_or_visit == "value" {
5362
self.stylevalue_matches.insert(capture.to_string());
5463
}
5564
self.visit_matches.insert(capture.to_string());
65+
} else {
66+
dbg!(&line);
67+
panic!("#[visit] or #[value] on unknown");
5668
}
5769
true
5870
})?;
@@ -62,19 +74,39 @@ impl<'a> Sink for NodeMatcher<'a> {
6274

6375
fn main() {
6476
println!("cargo::rerun-if-changed=build.rs");
65-
77+
use std::time::Instant;
78+
let now = Instant::now();
6679
let matcher = RegexMatcherBuilder::new()
6780
.multi_line(true)
6881
.dot_matches_new_line(true)
69-
// .build(r#"#\[value.*pub (?:struct|enum) (\w*(:?<'a>)?)"#)
82+
.ignore_whitespace(true)
7083
.build(
71-
r#"^\s*#\[(value|visit).*?(?:pub (?:struct|enum) |(?:ranged|boolean|discrete)_feature!\()(\w*(:?<'a>)?)"#,
84+
r#"
85+
# match the #[value] or #[visit] attribute
86+
^\s*\#\[(value|visit)
87+
# munch the data between the attribute and the definition
88+
.*?
89+
(
90+
# Is this a public definition?
91+
pub\s*(?:struct|enum)\s*
92+
|
93+
# Or one of the parser macros that create public definitions?
94+
(:?
95+
keyword_set!|
96+
pseudo_(?:class|element)!|
97+
(?:ranged|boolean|discrete)_feature!
98+
)\(
99+
)
100+
# munch any comments/attributes between this and our name (for macros)
101+
(:?\n?\s*(:?\/\/|\#)[^\n]*)*
102+
# finally grab the word (plus any lifetime definition)
103+
\s*(\w*(:?<'a>)?)"#,
72104
)
73105
.unwrap();
74106
let mut visit_matches = HashSet::new();
75107
let mut stylevalue_matches = HashSet::new();
76108
let mut searcher = SearcherBuilder::new().line_number(false).multi_line(true).build();
77-
for entry in glob("src/css/**/*.rs").unwrap() {
109+
for entry in glob("src/**/*.rs").unwrap() {
78110
let str = &entry.as_ref().unwrap().display();
79111
println!("cargo::rerun-if-changed={}", str);
80112
let context = NodeMatcher {
@@ -121,14 +153,17 @@ fn main() {
121153
}}",
122154
stylevalue_matches.iter().fold(String::new(), |mut out, prop| {
123155
let variant_name = prop.trim_end_matches("<'a>").trim_end_matches("StyleValue").to_string();
124-
let mut atom_name = kebab(variant_name.to_owned());
125-
if atom_name.starts_with("webkit") {
126-
atom_name = format!("-{}", atom_name);
156+
let mut variant_str = kebab(variant_name.to_owned());
157+
if variant_str.starts_with("webkit") {
158+
variant_str = format!("-{}", variant_str);
127159
}
128-
writeln!(out, "\t\t\t\t\t{}: {} = atom!(\"{}\"),", variant_name, prop, atom_name).unwrap();
160+
writeln!(out, "\t\t\t\t\t{}: {} = \"{}\",", variant_name, prop, variant_str).unwrap();
129161
out
130162
})
131163
);
132164

133165
let _ = write(Path::new(&env::var("OUT_DIR").unwrap()).join("css_apply_properties.rs"), source);
166+
167+
let elapsed = now.elapsed();
168+
println!("cargo::warning=Built in {:.?}", &elapsed);
134169
}

crates/css_ast/src/lib.rs

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
mod properties;
2+
mod rules;
3+
mod selector;
4+
mod specificity;
5+
mod stylerule;
6+
mod stylesheet;
7+
mod traits;
8+
mod types;
9+
mod units;
10+
mod values;
11+
mod visit;
12+
13+
pub use properties::*;
14+
pub use rules::*;
15+
pub use selector::*;
16+
pub use stylerule::*;
17+
pub use stylesheet::*;
18+
pub use types::*;
19+
pub use units::*;
20+
pub use values::*;
21+
pub use visit::*;
22+
23+
use css_lexer::Span;
24+
use css_parse::{diagnostics, CursorSink, Parse, Parser, Result as ParserResult, ToCursors};
25+
26+
// TODO! - delete this when we're done ;)
27+
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
28+
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(untagged))]
29+
pub enum Todo {
30+
#[default]
31+
Todo,
32+
}
33+
34+
impl<'a> Parse<'a> for Todo {
35+
fn parse(p: &mut Parser<'a>) -> ParserResult<Self> {
36+
Err(diagnostics::Unimplemented(Span::new(p.offset(), p.offset())))?
37+
}
38+
}
39+
40+
impl ToCursors for Todo {
41+
fn to_cursors(&self, _: &mut impl CursorSink) {}
42+
}
43+
44+
impl<'a> Visitable<'a> for Todo {
45+
fn accept<V: Visit<'a>>(&self, _: &mut V) {}
46+
}

0 commit comments

Comments
 (0)