diff --git a/src/atrule/mod.rs b/src/atrule/mod.rs index 37170cb..3f7a0d5 100644 --- a/src/atrule/mod.rs +++ b/src/atrule/mod.rs @@ -32,6 +32,7 @@ pub(crate) enum AtRule { Content, Unknown(UnknownAtRule), For(Vec), + If(Vec, Vec, Vec), } impl AtRule { @@ -104,7 +105,46 @@ impl AtRule { } AtRuleKind::Each => todo!("@each not yet implemented"), AtRuleKind::Extend => todo!("@extend not yet implemented"), - AtRuleKind::If => todo!("@if not yet implemented"), + AtRuleKind::If => { + let mut cond = Vec::new(); + let mut n = 0; + while let Some(tok) = toks.peek() { + match tok.kind { + TokenKind::Symbol(Symbol::OpenCurlyBrace) => n += 1, + TokenKind::Symbol(Symbol::CloseCurlyBrace) => n -= 1, + TokenKind::Interpolation => n += 1, + _ => {} + } + if n == 1 { + break; + } + cond.push(toks.next().unwrap()); + } + toks.next(); + devour_whitespace_or_comment(toks); + let mut yes = Vec::new(); + yes.extend(eat_stmts(toks, scope, super_selector)?); + + devour_whitespace_or_comment(toks); + + let mut no = Vec::new(); + if let Some(tok) = toks.peek() { + if tok.kind == TokenKind::AtRule(AtRuleKind::Else) { + toks.next(); + devour_whitespace_or_comment(toks); + if let Some(tok) = toks.next() { + if !tok.is_symbol(Symbol::OpenCurlyBrace) { + return Err("expected \"{\".".into()); + } + } + devour_whitespace_or_comment(toks); + no.extend(eat_stmts(toks, scope, super_selector)?); + } + } + devour_whitespace_or_comment(toks); + + AtRule::If(cond, yes, no) + } AtRuleKind::Else => todo!("@else not yet implemented"), AtRuleKind::For => { let mut stmts = Vec::new(); diff --git a/src/css.rs b/src/css.rs index bdc5955..d6a03d4 100644 --- a/src/css.rs +++ b/src/css.rs @@ -153,7 +153,7 @@ impl Css { .pretty_print(buf, nesting + 1)?; writeln!(buf, "{}}}", padding)?; } - _ => todo!(), + _ => todo!("at-rule other than unknown at toplevel"), }, Toplevel::Newline => { if has_written { diff --git a/src/lib.rs b/src/lib.rs index ad6a0b6..e0b566c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -417,6 +417,17 @@ impl<'a> StyleSheetParser<'a> { } AtRule::For(s) => rules.extend(s), AtRule::Content => return Err("@content is only allowed within mixin declarations.".into()), + AtRule::If(cond, yes, no) => { + if Value::from_tokens( + &mut cond.into_iter().peekable(), + &mut GLOBAL_SCOPE.with(|s| s.borrow().clone()), + &Selector::new(), + )?.is_true()? { + rules.extend(yes); + } else { + rules.extend(no); + } + } u @ AtRule::Unknown(..) => rules.push(Stmt::AtRule(u)), } } @@ -443,6 +454,19 @@ impl<'a> StyleSheetParser<'a> { Expr::Style(s) => stmts.push(Stmt::Style(s)), Expr::AtRule(a) => match a { AtRule::For(s) => stmts.extend(s), + AtRule::If(cond, yes, no) => { + if Value::from_tokens( + &mut cond.into_iter().peekable(), + &mut GLOBAL_SCOPE.with(|s| s.borrow().clone()), + &Selector::new(), + )? + .is_true()? + { + stmts.extend(yes); + } else { + stmts.extend(no); + } + } AtRule::Content => { return Err("@content is only allowed within mixin declarations.".into()) } @@ -620,6 +644,7 @@ pub(crate) fn eat_expr>( AtRule::Content => { return Err("@content is only allowed within mixin declarations.".into()) } + f @ AtRule::If(..) => Ok(Some(Expr::AtRule(f))), f @ AtRule::For(..) => Ok(Some(Expr::AtRule(f))), u @ AtRule::Unknown(..) => Ok(Some(Expr::AtRule(u))), };