Skip to content

Commit

Permalink
fix a variety of ComponentValues bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
keithamus committed Feb 13, 2024
1 parent 54b63d6 commit 66da5f0
Show file tree
Hide file tree
Showing 9 changed files with 104 additions and 96 deletions.
135 changes: 89 additions & 46 deletions crates/hdx_ast/src/css/component_values.rs
Original file line number Diff line number Diff line change
@@ -1,36 +1,11 @@
use hdx_atom::Atom;
use hdx_lexer::{PairWise, Token};
use hdx_parser::{unexpected, Box, Parse, Parser, Result as ParserResult, Spanned, State, Vec};
use hdx_parser::{unexpected, Box, Parse, Parser, Result as ParserResult, Spanned, State, Vec, expect};
use hdx_writer::{CssWriter, Result as WriterResult, WriteCss};
#[cfg(feature = "serde")]
use serde::Serialize;

// https://drafts.csswg.org/css-syntax-3/#consume-component-value
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize), serde(untagged))]
pub enum ComponentValue<'a> {
SimpleBlock(Spanned<SimpleBlock<'a>>),
Function(Spanned<Function<'a>>),
Token(Token),
}

// https://drafts.csswg.org/css-syntax-3/#consume-component-value
impl<'a> Parse<'a> for ComponentValue<'a> {
fn parse(parser: &mut Parser<'a>) -> ParserResult<Spanned<Self>> {
let span = parser.span();
match parser.cur() {
Token::LeftCurly | Token::LeftSquare | Token::LeftParen => {
Ok(Self::SimpleBlock(SimpleBlock::parse(parser)?).spanned(span.end(parser.pos())))
}
Token::Function(_) => Ok(Self::Function(Function::parse(parser)?).spanned(span.end(parser.pos()))),
token => {
parser.advance();
Ok(Self::Token(token).spanned(span))
}
}
}
}

// https://drafts.csswg.org/css-syntax-3/#consume-list-of-components
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize), serde())]
pub struct ComponentValues<'a>(pub Vec<'a, Spanned<ComponentValue<'a>>>);
Expand All @@ -43,15 +18,11 @@ impl<'a> Parse<'a> for ComponentValues<'a> {
loop {
match parser.cur() {
Token::Eof => break,
Token::RightCurly if parser.is(State::Nested) => break,
// ComponentValues can be passed a "stop token" which could be any token.
// In reality it is only ever called with a comma-token or semicolon-token.
Token::Semicolon if parser.is(State::StopOnSemicolon) => break,
Token::Comma if parser.is(State::StopOnComma) => break,
Token::RightCurly => {
parser.advance();
}
c => values.push(ComponentValue::parse(parser)?),
_ => values.push(ComponentValue::parse(parser)?),
}
}
Ok(Self(values).spanned(span.end(parser.pos())))
Expand All @@ -60,7 +31,36 @@ impl<'a> Parse<'a> for ComponentValues<'a> {

impl<'a> WriteCss<'a> for ComponentValues<'a> {
fn write_css<W: CssWriter>(&self, sink: &mut W) -> WriterResult {
todo!();
for value in &self.0 {
value.write_css(sink)?;
}
Ok(())
}
}

// https://drafts.csswg.org/css-syntax-3/#consume-component-value
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize), serde(untagged))]
pub enum ComponentValue<'a> {
SimpleBlock(Spanned<SimpleBlock<'a>>),
Function(Spanned<Function<'a>>),
Token(Token),
}

// https://drafts.csswg.org/css-syntax-3/#consume-component-value
impl<'a> Parse<'a> for ComponentValue<'a> {
fn parse(parser: &mut Parser<'a>) -> ParserResult<Spanned<Self>> {
let span = parser.span();
match parser.cur() {
Token::LeftCurly | Token::LeftSquare | Token::LeftParen => {
Ok(Self::SimpleBlock(SimpleBlock::parse(parser)?).spanned(span.end(parser.pos())))
}
Token::Function(_) => Ok(Self::Function(Function::parse(parser)?).spanned(span.end(parser.pos()))),
token => {
parser.advance_including_whitespace();
Ok(Self::Token(token).spanned(span))
}
}
}
}

Expand Down Expand Up @@ -128,17 +128,30 @@ impl<'a> WriteCss<'a> for ComponentValue<'a> {
#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type"))]
pub struct SimpleBlock<'a> {
pub pairwise: PairWise,
pub value: Box<'a, Spanned<ComponentValues<'a>>>,
pub values: Vec<'a, Spanned<ComponentValue<'a>>>
}

// https://drafts.csswg.org/css-syntax-3/#consume-a-simple-block
impl<'a> Parse<'a> for SimpleBlock<'a> {
fn parse(parser: &mut Parser<'a>) -> ParserResult<Spanned<Self>> {
if let Some(pairwise) = parser.cur().to_pairwise() {
let ending_token = pairwise.end();
let span = parser.span();
let value = ComponentValues::parse(parser)?;
Ok(Self { value: parser.boxup(value), pairwise }.spanned(span.end(parser.pos())))
let mut values = parser.new_vec();
let ending_token = pairwise.end();
parser.advance();
loop {
match parser.cur() {
Token::Eof => break,
t if t == ending_token => break,
_ => values.push(ComponentValue::parse(parser)?),
}
}
if parser.cur() == pairwise.end() {
parser.advance();
} else {
unexpected!(parser)
}
Ok(Self { values, pairwise }.spanned(span.end(parser.pos())))
} else {
unexpected!(parser)
}
Expand All @@ -152,7 +165,9 @@ impl<'a> WriteCss<'a> for SimpleBlock<'a> {
PairWise::Curly => sink.write_char('{')?,
PairWise::Paren => sink.write_char('(')?,
}
self.value.write_css(sink)?;
for value in &self.values {
value.write_css(sink)?;
}
match self.pairwise {
PairWise::Square => sink.write_char(']')?,
PairWise::Curly => sink.write_char('}')?,
Expand All @@ -166,17 +181,27 @@ impl<'a> WriteCss<'a> for SimpleBlock<'a> {
#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type"))]
pub struct Function<'a> {
pub name: Atom,
pub value: Box<'a, Spanned<ComponentValues<'a>>>,
pub values: Vec<'a, Spanned<ComponentValue<'a>>>
}

// https://drafts.csswg.org/css-syntax-3/#consume-function
impl<'a> Parse<'a> for Function<'a> {
fn parse(parser: &mut Parser<'a>) -> ParserResult<Spanned<Self>> {
let span = parser.span();
match parser.cur() {
Token::Function(name) => {
let value = ComponentValues::parse(parser)?;
Ok(Self { name, value: parser.boxup(value) }.spanned(span.end(parser.pos())))
let span = parser.span();
let mut values = parser.new_vec();
parser.advance();
loop {
match parser.cur() {
Token::Eof => break,
Token::RightParen => break,
_ => values.push(ComponentValue::parse(parser)?),
}
}
expect!(parser, Token::RightParen);
parser.advance();
Ok(Self { name, values }.spanned(span.end(parser.pos())))
}
token => unexpected!(parser, token),
}
Expand All @@ -187,21 +212,39 @@ impl<'a> WriteCss<'a> for Function<'a> {
fn write_css<W: CssWriter>(&self, sink: &mut W) -> WriterResult {
sink.write_str(self.name.as_ref())?;
sink.write_char('(')?;
self.value.write_css(sink)?;
for value in &self.values {
value.write_css(sink)?;
}
sink.write_char(')')
}
}

#[cfg(test)]
mod tests {
use oxc_allocator::Allocator;

use super::*;
use crate::test_helpers::test_write;

#[test]
fn size_test() {
use std::mem::size_of;
assert_eq!(size_of::<ComponentValue>(), 32);
assert_eq!(size_of::<SimpleBlock>(), 16);
assert_eq!(size_of::<Function>(), 16);
assert_eq!(size_of::<ComponentValues>(), 32);
assert_eq!(size_of::<ComponentValue>(), 56);
assert_eq!(size_of::<SimpleBlock>(), 40);
assert_eq!(size_of::<Function>(), 40);
}

#[test]
fn test_writes() {
let allocator = Allocator::default();
test_write::<ComponentValue>(&allocator, "foo", "foo");
test_write::<SimpleBlock>(&allocator, "[foo]", "[foo]");
test_write::<SimpleBlock>(&allocator, "(one two three)", "(one two three)");
test_write::<SimpleBlock>(&allocator, "(one(two))", "(one(two))");
test_write::<SimpleBlock>(&allocator, "{one(two)}", "{one(two)}");
test_write::<Function>(&allocator, "one((two) three)", "one((two)three)");
test_write::<ComponentValues>(&allocator, "a b c d", "a b c d");
test_write::<ComponentValues>(&allocator, "body { color: black }", "body {color: black }");
}
}
3 changes: 2 additions & 1 deletion crates/hdx_ast/src/css/selector/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,7 @@ mod test {
// test_write::<Component>(&allocator, "*|x", "*|x");
test_write::<Selector>(&allocator, ":root", ":root");
test_write::<Selector>(&allocator, "body [attr|='foo']", "body [attr|=\"foo\"]");
// test_write::<Selector>(&allocator, "*|x :focus-within", "*|x :focus-within");
// test_write::<Selector>(&allocator, "*|x :focus-within", "*|x
// :focus-within");
}
}
1 change: 1 addition & 0 deletions crates/hdx_ast/src/css/stylerule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,5 +116,6 @@ mod test {
fn test_writes() {
let allocator = Allocator::default();
test_write::<StyleRule>(&allocator, "body {}", "body{}");
test_write::<StyleRule>(&allocator, "body { --foo {} }", "body{ --foo {} }");
}
}
1 change: 0 additions & 1 deletion crates/hdx_ast/src/css/values/display/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,6 @@ impl<'a> WriteCss<'a> for Display {
#[cfg(test)]
mod tests {

use hdx_writer::{BaseCssWriter, WriteCss};
use oxc_allocator::Allocator;

use super::*;
Expand Down
2 changes: 1 addition & 1 deletion crates/hdx_ast/src/css/values/sizing/width.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ mod tests {
#[test]
fn size_test() {
use std::mem::size_of;
assert_eq!(size_of::<Width>(), 16);
assert_eq!(size_of::<Width>(), 12);
}

#[test]
Expand Down
32 changes: 0 additions & 32 deletions crates/hdx_ast/src/css/values/units/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
#[cfg(feature = "serde")]
use serde::Serialize;

use crate::Writable;

mod angles;
mod custom;
mod float;
Expand All @@ -21,33 +16,6 @@ pub use percent::*;
pub use resolution::*;
pub use time::*;

#[derive(Writable, Debug, Clone, Copy, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize), serde())]
pub enum CSSNumeric {
Length(Length),
Angle(Angle),
Time(Time),
Frequency(Frequency),
Resolution(Resolution),
#[writable(suffix = "fr")]
Flex(CSSFloat),
Percent(Percent),
}

impl Into<CSSFloat> for CSSNumeric {
fn into(self) -> CSSFloat {
match self {
Self::Length(v) => v.into(),
Self::Angle(v) => v.into(),
Self::Time(v) => v.into(),
Self::Frequency(v) => v.into(),
Self::Resolution(v) => v.into(),
Self::Flex(v) => v.into(),
Self::Percent(v) => v.into(),
}
}
}

pub trait AbsoluteUnit: Unit {
fn to_base(&self) -> Self;
}
Expand Down
4 changes: 0 additions & 4 deletions crates/hdx_lexer/src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,8 +218,6 @@ impl Token {
pub fn is_signed(&self) -> bool {
match self {
Self::Number(_, ty) => ty.is_signed(),
Self::Number(_, ty) => ty.is_signed(),
Self::Dimension(_, _, ty) => ty.is_signed(),
Self::Dimension(_, _, ty) => ty.is_signed(),
_ => false,
}
Expand All @@ -228,8 +226,6 @@ impl Token {
pub fn is_int(&self) -> bool {
match self {
Self::Number(_, ty) => ty.is_int(),
Self::Number(_, ty) => ty.is_int(),
Self::Dimension(_, _, ty) => ty.is_int(),
Self::Dimension(_, _, ty) => ty.is_int(),
_ => false,
}
Expand Down
6 changes: 3 additions & 3 deletions crates/hdx_parser/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ impl Default for Features {

#[bitmask(u8)]
pub enum State {
Nested = 0x01,
Nested = 0b0000_0001,

// Stop Tokens for some algorithms
StopOnSemicolon = 0xF0,
StopOnComma = 0xF1,
StopOnSemicolon = 0b1000_0000,
StopOnComma = 0b1100_0000,
}

pub struct ParserReturn<T> {
Expand Down
16 changes: 8 additions & 8 deletions crates/hdx_parser/src/traits.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
use hdx_atom::Atom;
use hdx_lexer::Token;

use crate::{
diagnostics, discard, expect,
expect,
parser::Parser,
span::{Span, Spanned},
unexpected, Result,
Vec
unexpected, Result, Vec,
};

pub trait Parse<'a>: Sized {
Expand Down Expand Up @@ -92,7 +90,9 @@ pub trait DeclarationRuleList<'a>: Sized + Parse<'a> {
type AtRule: AtRule<'a> + Parse<'a>;

// https://drafts.csswg.org/css-syntax-3/#consume-a-qualified-rule
fn parse_declaration_rule_list(parser: &mut Parser<'a>) -> Result<(Vec<'a, Spanned<Self::Declaration>>, Vec<'a, Spanned<Self::AtRule>>)> {
fn parse_declaration_rule_list(
parser: &mut Parser<'a>,
) -> Result<(Vec<'a, Spanned<Self::Declaration>>, Vec<'a, Spanned<Self::AtRule>>)> {
expect!(parser, Token::LeftCurly);
parser.advance();
let mut declarations = parser.new_vec();
Expand All @@ -101,15 +101,15 @@ pub trait DeclarationRuleList<'a>: Sized + Parse<'a> {
match parser.cur() {
Token::AtKeyword(_) => {
rules.push(Self::AtRule::parse(parser)?);
},
}
Token::Ident(_) => {
declarations.push(Self::Declaration::parse(parser)?);
},
}
Token::RightCurly => {
parser.advance();
return Ok((declarations, rules));
}
token => unexpected!(parser, token)
token => unexpected!(parser, token),
}
}
}
Expand Down

0 comments on commit 66da5f0

Please sign in to comment.