seam

Symbolic-Expressions As Markup.
git clone git://git.knutsen.co/seam
Log | Files | Refs | README | LICENSE

commit 14a067d52b94acb24c059e1885d08759a56520f3
parent 7a08dc915e546affdd3679d8011253819c8c29ba
Author: Demonstrandum <samuel@knutsen.co>
Date:   Wed,  3 Jul 2024 16:35:20 +0100

Handle special `@`-rule selectors that don't have nested rules.

Diffstat:
MCargo.lock | 2+-
MCargo.toml | 2+-
Msrc/assemble/css.rs | 66++++++++++++++++++++++++++++++++++++------------------------------
Msrc/lib.rs | 2+-
4 files changed, 39 insertions(+), 33 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -162,7 +162,7 @@ dependencies = [ [[package]] name = "seam" -version = "0.2.2" +version = "0.2.3" dependencies = [ "chrono", "colored", diff --git a/Cargo.toml b/Cargo.toml @@ -4,7 +4,7 @@ description = "Symbolic Expressions As Markup." keywords = ["markup", "lisp", "macro", "symbolic-expression", "sexp"] license-file = "LICENSE" homepage = "https://git.knutsen.co/seam" -version = "0.2.2" +version = "0.2.3" authors = ["Demonstrandum <samuel@knutsen.co>"] edition = "2021" diff --git a/src/assemble/css.rs b/src/assemble/css.rs @@ -18,9 +18,9 @@ impl<'a> CSSFormatter<'a> { pub const DEFAULT : &str = "\n"; /// All CSS functions, I might have missed a few. -const CSS_FUNCTIONS : [&str; 57] = [ +const CSS_FUNCTIONS : [&str; 58] = [ "attr", "blur", "brightness", "calc", "circle", "color", "contrast", - "counter", "counters", "cubic-bezier", "drop-shadow", "ellipse", + "counter", "counters", "cubic-bezier", "drop-shadow", "ellipse", "format", "grayscale", "hsl", "hsla", "hue-rotate", "hwb", "image", "inset", "invert", "lab", "lch", "linear-gradient", "matrix", "matrix3d", "opacity", "perspective", "polygon", "radial-gradient", @@ -47,12 +47,19 @@ const CSS_SPECIAL_SELECTORS : [&str; 12] ]; /// Special selectors that do not have a body. -const CSS_ONELINE_RULES : [&str; 3] +const CSS_ONELINE_RULES: [&str; 3] = [ CSS_SPECIAL_SELECTORS[0] //< @charset , CSS_SPECIAL_SELECTORS[5] //< @import , CSS_SPECIAL_SELECTORS[8] //< @namespace ]; +/// Special selectors that do not have nested selectors as their body. +const CSS_NON_NESTED_SELECTORS: [&str; 3] + = [ CSS_SPECIAL_SELECTORS[3] //< @font-face + , CSS_SPECIAL_SELECTORS[9] //< @page + , CSS_SPECIAL_SELECTORS[10] //< @property + ]; + /// The only four math operations supported by CSS calc(...), /// or at least I think. const BINARY_OPERATORS : [&str; 4] = ["+", "-", "*", "/"]; @@ -119,16 +126,27 @@ pub fn css_value<'a>(_property : &str, node: &'a ParseNode<'a>) /// # A special-selector / @-rule looks like: /// S-expr: CSS: /// (@symbol arg) -> @symbol arg; -/// (@symbol :attr arg) -> @symbol (attr: arg); +/// (@symbol :attr arg) -> @symbol (attr: arg); OR @symbol { attr: arg } /// (@symbol (select :prop val)) -> @symbol { select { prop: val; } } /// (@sym x :attr arg (sel :prop val)) -> @sym x (attr: arg) { sel { prop: val; } } fn generate_special_selector<'a>(f: Formatter, - selector: &str, - arguments: Iter<'a, ParseNode<'a>>) - -> Result<(), GenerationError<'a>> { - // Deal with oneline rules quickly. + selector: &str, + mut arguments: Iter<'a, ParseNode<'a>>) +-> Result<(), GenerationError<'a>> { + let mut parsing_rules: bool = false; + let unexpected_node = |node: &'a ParseNode<'a>, rules: bool| { + if rules { + Err(GenerationError::new("CSS", + "Expected list (i.e. a CSS rule) here!", + &node.site())) + } else { + Ok(()) + } + }; + // Deal with one-line rules quickly. if CSS_ONELINE_RULES.contains(&selector) { write!(f, "{} ", selector)?; + arguments.next(); // Skip `@`-selector. for arg in arguments { match arg { ParseNode::Attribute { ref keyword, node, .. } => { @@ -140,19 +158,10 @@ fn generate_special_selector<'a>(f: Formatter, writeln!(f, ";")?; return Ok(()); } - // @-rules with nested elements! - write!(f, "{} ", selector)?; - let mut parsing_rules = false; - let unexpected_node = |node: &'a ParseNode<'a>, rules: bool| { - if rules { - Err(GenerationError::new("CSS", - "Expected list (i.e. a CSS rule) here!", - &node.site())) - } else { - Ok(()) - } - }; + // Handle @-rules with nested elements. + write!(f, "{} ", selector)?; + arguments.next(); // Skip `@`-selector. for arg in arguments { match arg { @@ -167,7 +176,7 @@ fn generate_special_selector<'a>(f: Formatter, } parsing_rules = true; write!(f, "{}", leading_whitespace)?; - generate_css_rule(f, rule)?; + generate_css_rule(f, rule.into_iter())?; }, _ => { unexpected_node(&arg, parsing_rules)?; @@ -179,8 +188,7 @@ fn generate_special_selector<'a>(f: Formatter, Ok(()) } -fn generate_css_rule<'a>(f: Formatter, list: &'a [ParseNode<'a>]) -> Result<(), GenerationError<'a>> { - let mut iter = list.iter(); +fn generate_css_rule<'a>(f: Formatter, iter: Iter<'a, ParseNode<'a>>) -> Result<(), GenerationError<'a>> { let mut prop_i = 0; // Index of first property. // TODO: Selector functions such as nth-child(...), etc. // e.g. (ul li(:nth-child (+ 2n 1))) -> ul li:nth-child(2n + 1). @@ -197,12 +205,13 @@ fn generate_css_rule<'a>(f: Formatter, list: &'a [ParseNode<'a>]) -> Result<(), return Err(GenerationError::new("CSS", "CSS selector(s) missing. \ Expected a symbol/identifier node, none was found!", - &list[0].site())); + &selectors.peek().unwrap().site)); }; // Handle special @-rule selectors. - if CSS_SPECIAL_SELECTORS.contains(&head.value.as_ref()) { - iter.next(); //< Throw away the head. + if CSS_SPECIAL_SELECTORS.contains(&head.value.as_ref()) + && !CSS_NON_NESTED_SELECTORS.contains(&head.value.as_ref()) { + // Don't do anything special for non-nested selectors. return generate_special_selector(f, &head.value, iter); } @@ -245,7 +254,7 @@ impl<'a> MarkupDisplay for CSSFormatter<'a> { match node { ParseNode::List { nodes: list, leading_whitespace, .. } => { write!(f, "{}", leading_whitespace)?; - generate_css_rule(f, &*list)?; + generate_css_rule(f, list.into_iter())?; }, ParseNode::Attribute { site, .. } => { return Err(GenerationError::new("CSS", @@ -258,9 +267,6 @@ impl<'a> MarkupDisplay for CSSFormatter<'a> { | ParseNode::Number(node) | ParseNode::String(node) => { let site = node.site.to_owned(); - if node.value.trim().is_empty() { - continue; - } return Err(GenerationError::new("CSS", "Symbolic node not expected here, CSS documents \ are supposed to be a series of selectors \ diff --git a/src/lib.rs b/src/lib.rs @@ -9,7 +9,7 @@ use parse::{expander, parser, lexer}; use std::{fs, io, path::Path}; -pub const VERSION: (u8, u8, u8) = (0, 2, 2); +pub const VERSION: (u8, u8, u8) = (0, 2, 3); pub fn tree_builder<'a, P: AsRef<Path>>(source_path: Option<P>, string: String) -> expander::Expander<'a> {