seam

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

commit 13f0869f58bf5e33d59b48795f35abbe51c43d5f
parent e1facb08b722bfef09ccf8ccd2acfb09adf11742
Author: Demonstrandum <moi@knutsen.co>
Date:   Thu,  2 Jul 2020 22:17:34 +0100

Working (but naïve) CSS generation.

Diffstat:
Msamples/css-concept.sex | 2+-
Msrc/assemble/css.rs | 73++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Mtest-css.sex | 6++++--
3 files changed, 75 insertions(+), 6 deletions(-)

diff --git a/samples/css-concept.sex b/samples/css-concept.sex @@ -13,7 +13,7 @@ ;p #para-id { ; width: calc(3em + 6px); ; color: green; -; margin: 3px, auto, 4px, 2px; +; margin: 3px auto 4px 2px; ; position: absolute; ;} diff --git a/src/assemble/css.rs b/src/assemble/css.rs @@ -17,11 +17,78 @@ impl CSSFormatter { pub const DEFAULT : &str = "\n"; +/// All CSS functions, I might have missed a few. +const CSS_FUNCTIONS : [&str; 52] = [ + "attr", "blur", "brightness", "calc", "circle", "contrast", + "counter", "counters", "cubic-bezier", "drop-shadow", "ellipse", + "grayscale", "hsl", "hsla", "hue-rotate", "hwb", "image", "inset", + "invert", "linear-gradient", "matrix", "matrix3d", "opacity", + "perspective", "polygon", "radial-gradient", "repeating-linear-gradient", + "repeating-radial-gradient", "rgb", "rgba", "rotate", "rotate3d", + "rotateX", "rotateY", "rotateZ", "saturate", "sepia", "scale", "scale3d", + "scaleX", "scaleY", "scaleZ", "skew", "skewX", "skewY", "symbols", + "translate", "translate3d", "translateX", "translateY", "translateZ", + "url", +]; + +/// The only four math operations supported by CSS calc(...), +/// or at least I think. +const BINARY_OPERATORS : [&str; 4] = ["+", "-", "*", "/"]; + +fn convert_value(node : &ParseNode) -> Result<String, GenerationError> { + match node { + ParseNode::List(list) => { + let list = parser::strip(list, false); + let result = match list.as_slice() { + [head, tail@..] => { + let head = convert_value(head)?; + + let mut tail_tmp = vec![String::new(); tail.len()]; + for (i, e) in tail.iter().enumerate() { + tail_tmp[i] = convert_value(e)? + } + let tail = tail_tmp.as_slice(); + + let args = tail.iter() + .fold(String::new(), |acc, s| acc + " " + &s); + let args = args.trim(); + if CSS_FUNCTIONS.contains(&head.as_str()) { + format!("{}({})", head, args) + } else if BINARY_OPERATORS.contains(&head.as_str()) { + let args = tail + .join(&format!(" {} ", head)); + format!("({})", args) + } else { + format!("{} {}", head, args) + } + }, + _ => String::from("") + }; + Ok(result) + }, + ParseNode::Number(node) + | ParseNode::Symbol(node) + | ParseNode::String(node) => + Ok(if node.value.chars().any(|c| c.is_whitespace()) { + format!("\"{}\"", node.value) + } else { + node.value.to_owned() + }), + ParseNode::Attribute(_) => Err(GenerationError::new("CSS-value", + "Incompatible structure (attribute) found in CSS \ + property value.", + &node.site())) + } +} + /// Function responsible for translating a CSS value (i.e. /// a value of a CSS property) from some s-expression into /// a valid CSS value. -pub fn css_value(property : &str, node : &ParseNode) -> String { - String::from("x") +pub fn css_value(_property : &str, node : &ParseNode) +-> Result<String, GenerationError> { + // Naïve way (in future consider the type of property, + // and take care of special cases): + convert_value(node) } impl MarkupDisplay for CSSFormatter { @@ -69,7 +136,7 @@ impl MarkupDisplay for CSSFormatter { let value = &property.node; writeln!(f, " {}: {};", &property.keyword, - css_value(&property.keyword, value))?; + css_value(&property.keyword, value)?)?; } else { return Err(GenerationError::new("CSS", "CSS property-value pairs must be in the \ diff --git a/test-css.sex b/test-css.sex @@ -1,6 +1,8 @@ -(a b c +(a 2b c2 + :background-image (url dog.png) + :background-position ((calc (- 100% 50px)) (calc (- 100% 20px))) :width 2 :heigh 3) (x #y .z - :color (rgb 2 (calc (+ 3 1)) 4)) + :color (rgb 2 (calc (+ 3 (* 7 3) 1)) 4))