///|
/// Parsing for the `animation` / `transition` properties (shorthands and
/// longhands). Values are comma-separated lists; commas inside function
/// arguments (e.g. `cubic-bezier(...)`, `steps(...)`, `linear(...)`) are not
/// separators, so splitting is done at paren depth 0 on the token stream.
///|
/// Split a token view into comma-separated segments at paren depth 0.
fn split_top_level_commas(
tokens : ArrayView[@token.Token],
) -> Array[ArrayView[@token.Token]] {
let segments : Array[ArrayView[@token.Token]] = []
let mut depth = 0
let mut start = 0
for i = 0; i < tokens.length(); i = i + 1 {
match tokens[i] {
@token.Token::Function(_) | @token.Token::LeftParen => depth = depth + 1
@token.Token::RightParen => if depth > 0 { depth = depth - 1 }
@token.Token::Comma => if depth == 0 {
segments.push(tokens[start:i])
start = i + 1
}
_ => ()
}
}
segments.push(tokens[start:])
segments
}
///|
/// Reconstruct CSS source text for a token slice (used to feed sub-parsers such
/// as `parse_easing`). Only the token kinds that appear in animation/transition
/// values are handled.
fn reconstruct_css(tokens : ArrayView[@token.Token]) -> String {
let sb = StringBuilder::new()
for i = 0; i < tokens.length(); i = i + 1 {
match tokens[i] {
@token.Token::Ident(s) => sb.write_string(s)
@token.Token::Function(name) => {
sb.write_string(name)
sb.write_char('(')
}
@token.Token::Number(v, _) => sb.write_string(format_number(v))
@token.Token::Dimension(v, unit) => {
sb.write_string(format_number(v))
sb.write_string(unit)
}
@token.Token::Percentage(v) => {
sb.write_string(format_number(v))
sb.write_char('%')
}
@token.Token::LeftParen => sb.write_char('(')
@token.Token::RightParen => sb.write_char(')')
@token.Token::Comma => sb.write_char(',')
@token.Token::Whitespace => sb.write_char(' ')
@token.Token::Delim(c) => sb.write_char(c)
@token.Token::Colon => sb.write_char(':')
_ => ()
}
}
sb.to_string()
}
///|
/// Format a Double for CSS reconstruction, dropping a trailing `.0` for
/// integers (so `4.0` becomes `4`, matching the original source closely enough
/// for the sub-parsers).
fn format_number(v : Double) -> String {
let i = v.to_int()
if i.to_double() == v {
i.to_string()
} else {
v.to_string()
}
}
///|
/// Parse a single `