From 981bf27cb87734ac9df28a55ec44770db29dae92 Mon Sep 17 00:00:00 2001 From: ConnorSkees <39542938+ConnorSkees@users.noreply.github.com> Date: Mon, 23 Mar 2020 19:56:24 -0400 Subject: [PATCH] properly parse and emit bracketed lists --- src/builtin/color/hsl.rs | 4 ++-- src/builtin/color/rgb.rs | 4 ++-- src/builtin/list.rs | 24 +++++++++++----------- src/common.rs | 6 ++++++ src/value/mod.rs | 30 ++++++++++++++++++---------- src/value/parse.rs | 43 ++++++++++++++++++++++++++++++---------- tests/list.rs | 14 ++++++++----- 7 files changed, 83 insertions(+), 42 deletions(-) diff --git a/src/builtin/color/hsl.rs b/src/builtin/color/hsl.rs index b8014e0..f916c6c 100644 --- a/src/builtin/color/hsl.rs +++ b/src/builtin/color/hsl.rs @@ -18,7 +18,7 @@ pub(crate) fn register(f: &mut HashMap) { if args.len() == 1 { let mut channels = match arg!(args, 0, "channels") { - Value::List(v, _) => v, + Value::List(v, ..) => v, _ => return Err("Missing argument $channels.".into()), }; @@ -96,7 +96,7 @@ pub(crate) fn register(f: &mut HashMap) { if args.len() == 1 { let mut channels = match arg!(args, 0, "channels") { - Value::List(v, _) => v, + Value::List(v, ..) => v, _ => return Err("Missing argument $channels.".into()), }; diff --git a/src/builtin/color/rgb.rs b/src/builtin/color/rgb.rs index 0daf0ff..46e1c38 100644 --- a/src/builtin/color/rgb.rs +++ b/src/builtin/color/rgb.rs @@ -17,7 +17,7 @@ pub(crate) fn register(f: &mut HashMap) { if args.len() == 1 { let mut channels = match arg!(args, 0, "channels") { - Value::List(v, _) => v, + Value::List(v, ..) => v, _ => return Err("Missing argument $channels.".into()), }; @@ -139,7 +139,7 @@ pub(crate) fn register(f: &mut HashMap) { if args.len() == 1 { let mut channels = match arg!(args, 0, "channels") { - Value::List(v, _) => v, + Value::List(v, ..) => v, _ => return Err("Missing argument $channels.".into()), }; diff --git a/src/builtin/list.rs b/src/builtin/list.rs index 61b4a78..04702a7 100644 --- a/src/builtin/list.rs +++ b/src/builtin/list.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use num_traits::{One, Signed, ToPrimitive, Zero}; use super::Builtin; -use crate::common::{ListSeparator, QuoteKind}; +use crate::common::{Brackets, ListSeparator, QuoteKind}; use crate::unit::Unit; use crate::value::{Number, Value}; @@ -13,7 +13,7 @@ pub(crate) fn register(f: &mut HashMap) { Box::new(|args, _| { max_args!(args, 1); let len = match arg!(args, 0, "list") { - Value::List(v, _) => Number::from(v.len()), + Value::List(v, ..) => Number::from(v.len()), _ => Number::one(), }; Ok(Value::Dimension(len, Unit::None)) @@ -24,7 +24,7 @@ pub(crate) fn register(f: &mut HashMap) { Box::new(|args, _| { max_args!(args, 2); let list = match arg!(args, 0, "list") { - Value::List(v, _) => v, + Value::List(v, ..) => v, v => vec![v], }; let n = match arg!(args, 1, "n") { @@ -62,7 +62,7 @@ pub(crate) fn register(f: &mut HashMap) { max_args!(args, 1); Ok(Value::Ident( match arg!(args, 0, "list") { - Value::List(_, sep) => sep.name(), + Value::List(_, sep, ..) => sep.name(), _ => ListSeparator::Space.name(), } .to_owned(), @@ -75,7 +75,7 @@ pub(crate) fn register(f: &mut HashMap) { Box::new(|args, _| { max_args!(args, 3); let (mut list, sep) = match arg!(args, 0, "list") { - Value::List(v, sep) => (v, sep), + Value::List(v, sep, ..) => (v, sep), v => (vec![v], ListSeparator::Space), }; let n = match arg!(args, 1, "n") { @@ -107,7 +107,7 @@ pub(crate) fn register(f: &mut HashMap) { list[len - n.abs().to_integer().to_usize().unwrap()] = val; } - Ok(Value::List(list, sep)) + Ok(Value::List(list, sep, Brackets::None)) }), ); f.insert( @@ -115,7 +115,7 @@ pub(crate) fn register(f: &mut HashMap) { Box::new(|args, _| { max_args!(args, 3); let (mut list, sep) = match arg!(args, 0, "list") { - Value::List(v, sep) => (v, sep), + Value::List(v, sep, ..) => (v, sep), v => (vec![v], ListSeparator::Space), }; let val = arg!(args, 1, "val"); @@ -137,16 +137,16 @@ pub(crate) fn register(f: &mut HashMap) { list.push(val); - Ok(Value::List(list, sep)) + Ok(Value::List(list, sep, Brackets::None)) }), ); f.insert( "join".to_owned(), Box::new(|args, _| { max_args!(args, 3); - let (mut list1, sep1) = match arg!(args, 0, "list") { - Value::List(v, sep) => (v, sep), - v => (vec![v], ListSeparator::Space), + let (mut list1, sep1, brackets) = match arg!(args, 0, "list") { + Value::List(v, sep, brackets) => (v, sep, brackets), + v => (vec![v], ListSeparator::Space, Brackets::None), }; let list2 = match arg!(args, 1, "list") { Value::List(v, ..) => v, @@ -176,7 +176,7 @@ pub(crate) fn register(f: &mut HashMap) { list1.extend(list2); - Ok(Value::List(list1, sep)) + Ok(Value::List(list1, sep, brackets)) }), ); } diff --git a/src/common.rs b/src/common.rs index 0feab69..623dcef 100644 --- a/src/common.rs +++ b/src/common.rs @@ -383,6 +383,12 @@ impl Display for QuoteKind { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum Brackets { + None, + Bracketed, +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(crate) enum ListSeparator { Space, diff --git a/src/value/mod.rs b/src/value/mod.rs index 4259357..7505860 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -2,7 +2,7 @@ use std::fmt::{self, Display, Write}; use std::iter::Iterator; use crate::color::Color; -use crate::common::{ListSeparator, Op, QuoteKind}; +use crate::common::{Brackets, ListSeparator, Op, QuoteKind}; use crate::error::SassResult; use crate::unit::Unit; pub(crate) use number::Number; @@ -18,7 +18,7 @@ pub(crate) enum Value { False, Null, Dimension(Number, Unit), - List(Vec, ListSeparator), + List(Vec, ListSeparator, Brackets), Color(Color), UnaryOp(Op, Box), BinaryOp(Box, Op, Box), @@ -39,14 +39,24 @@ impl Display for Value { } _ => write!(f, "{}{}", num, unit), }, - Self::List(vals, sep) => write!( - f, - "{}", - vals.iter() - .map(std::string::ToString::to_string) - .collect::>() - .join(sep.as_str()) - ), + Self::List(vals, sep, brackets) => match brackets { + Brackets::None => write!( + f, + "{}", + vals.iter() + .map(std::string::ToString::to_string) + .collect::>() + .join(sep.as_str()), + ), + Brackets::Bracketed => write!( + f, + "[{}]", + vals.iter() + .map(std::string::ToString::to_string) + .collect::>() + .join(sep.as_str()), + ), + }, Self::Color(c) => write!(f, "{}", c), Self::UnaryOp(..) | Self::BinaryOp(..) => write!( f, diff --git a/src/value/parse.rs b/src/value/parse.rs index 8731e32..ea5a0ab 100644 --- a/src/value/parse.rs +++ b/src/value/parse.rs @@ -8,7 +8,7 @@ use num_traits::pow; use crate::args::eat_call_args; use crate::builtin::GLOBAL_FUNCTIONS; use crate::color::Color; -use crate::common::{Keyword, ListSeparator, Op, QuoteKind, Symbol}; +use crate::common::{Brackets, Keyword, ListSeparator, Op, QuoteKind, Symbol}; use crate::error::SassResult; use crate::scope::Scope; use crate::selector::Selector; @@ -82,19 +82,23 @@ impl Value { None => return Ok(left), }; match next.kind { - TokenKind::Symbol(Symbol::SemiColon) | TokenKind::Symbol(Symbol::CloseParen) => { - Ok(left) - } + TokenKind::Symbol(Symbol::SemiColon) + | TokenKind::Symbol(Symbol::CloseParen) + | TokenKind::Symbol(Symbol::CloseSquareBrace) => Ok(left), TokenKind::Symbol(Symbol::Comma) => { toks.next(); devour_whitespace_or_comment(toks); let right = Self::from_tokens(toks, scope, super_selector)?; - if let Value::List(v, ListSeparator::Comma) = right { + if let Value::List(v, ListSeparator::Comma, Brackets::None) = right { let mut v2 = vec![left]; v2.extend(v); - Ok(Value::List(v2, ListSeparator::Comma)) + Ok(Value::List(v2, ListSeparator::Comma, Brackets::None)) } else { - Ok(Value::List(vec![left, right], ListSeparator::Comma)) + Ok(Value::List( + vec![left, right], + ListSeparator::Comma, + Brackets::None, + )) } } TokenKind::Symbol(Symbol::Plus) @@ -120,12 +124,16 @@ impl Value { _ => { devour_whitespace_or_comment(toks); let right = Self::from_tokens(toks, scope, super_selector)?; - if let Value::List(v, ListSeparator::Space) = right { + if let Value::List(v, ListSeparator::Space, Brackets::None) = right { let mut v2 = vec![left]; v2.extend(v); - Ok(Value::List(v2, ListSeparator::Space)) + Ok(Value::List(v2, ListSeparator::Space, Brackets::None)) } else { - Ok(Value::List(vec![left, right], ListSeparator::Space)) + Ok(Value::List( + vec![left, right], + ListSeparator::Space, + Brackets::None, + )) } } } @@ -184,7 +192,11 @@ impl Value { devour_whitespace_or_comment(toks); if toks.peek().unwrap().is_symbol(Symbol::CloseParen) { toks.next(); - return Ok(Value::List(Vec::new(), ListSeparator::Space)); + return Ok(Value::List( + Vec::new(), + ListSeparator::Space, + Brackets::None, + )); } let val = Self::from_tokens(toks, scope, super_selector)?; let next = toks.next(); @@ -265,6 +277,15 @@ impl Value { | q @ TokenKind::Symbol(Symbol::SingleQuote) => { parse_quoted_string(toks, scope, &q, super_selector) } + TokenKind::Symbol(Symbol::OpenSquareBrace) => { + let inner = Self::from_tokens(toks, scope, super_selector)?; + devour_whitespace_or_comment(toks); + toks.next(); + Ok(match inner { + Value::List(v, sep, ..) => Value::List(v, sep, Brackets::Bracketed), + v => Value::List(vec![v], ListSeparator::Space, Brackets::Bracketed), + }) + } TokenKind::Variable(ref v) => Ok(scope.get_var(v)?), TokenKind::Interpolation => { let mut s = parse_interpolation(toks, scope, super_selector)?.to_string(); diff --git a/tests/list.rs b/tests/list.rs index 68fb57c..c0d7328 100644 --- a/tests/list.rs +++ b/tests/list.rs @@ -140,8 +140,12 @@ test!( "a {\n color: join((a, b), (c, d), space);\n}\n", "a {\n color: a b c d;\n}\n" ); -// test!( -// join_bracketed, -// "a {\n color: join([a], b);\n}\n", -// "a {\n color: [a b];\n}\n" -// ); +test!( + join_bracketed, + "a {\n color: join([a], b);\n}\n", + "a {\n color: [a b];\n}\n" +); +test!(bracketed_ident, "a {\n color: [a];\n}\n"); +test!(bracketed_space_list, "a {\n color: [a b];\n}\n"); +test!(bracketed_comma_list, "a {\n color: [a, b];\n}\n"); +test!(bracketed_as_space_list, "a {\n color: [a b] c;\n}\n");