properly parse and emit bracketed lists

This commit is contained in:
ConnorSkees 2020-03-23 19:56:24 -04:00
parent 9233b1d2ba
commit 981bf27cb8
7 changed files with 83 additions and 42 deletions

View File

@ -18,7 +18,7 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
if args.len() == 1 { if args.len() == 1 {
let mut channels = match arg!(args, 0, "channels") { let mut channels = match arg!(args, 0, "channels") {
Value::List(v, _) => v, Value::List(v, ..) => v,
_ => return Err("Missing argument $channels.".into()), _ => return Err("Missing argument $channels.".into()),
}; };
@ -96,7 +96,7 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
if args.len() == 1 { if args.len() == 1 {
let mut channels = match arg!(args, 0, "channels") { let mut channels = match arg!(args, 0, "channels") {
Value::List(v, _) => v, Value::List(v, ..) => v,
_ => return Err("Missing argument $channels.".into()), _ => return Err("Missing argument $channels.".into()),
}; };

View File

@ -17,7 +17,7 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
if args.len() == 1 { if args.len() == 1 {
let mut channels = match arg!(args, 0, "channels") { let mut channels = match arg!(args, 0, "channels") {
Value::List(v, _) => v, Value::List(v, ..) => v,
_ => return Err("Missing argument $channels.".into()), _ => return Err("Missing argument $channels.".into()),
}; };
@ -139,7 +139,7 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
if args.len() == 1 { if args.len() == 1 {
let mut channels = match arg!(args, 0, "channels") { let mut channels = match arg!(args, 0, "channels") {
Value::List(v, _) => v, Value::List(v, ..) => v,
_ => return Err("Missing argument $channels.".into()), _ => return Err("Missing argument $channels.".into()),
}; };

View File

@ -3,7 +3,7 @@ use std::collections::HashMap;
use num_traits::{One, Signed, ToPrimitive, Zero}; use num_traits::{One, Signed, ToPrimitive, Zero};
use super::Builtin; use super::Builtin;
use crate::common::{ListSeparator, QuoteKind}; use crate::common::{Brackets, ListSeparator, QuoteKind};
use crate::unit::Unit; use crate::unit::Unit;
use crate::value::{Number, Value}; use crate::value::{Number, Value};
@ -13,7 +13,7 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
Box::new(|args, _| { Box::new(|args, _| {
max_args!(args, 1); max_args!(args, 1);
let len = match arg!(args, 0, "list") { let len = match arg!(args, 0, "list") {
Value::List(v, _) => Number::from(v.len()), Value::List(v, ..) => Number::from(v.len()),
_ => Number::one(), _ => Number::one(),
}; };
Ok(Value::Dimension(len, Unit::None)) Ok(Value::Dimension(len, Unit::None))
@ -24,7 +24,7 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
Box::new(|args, _| { Box::new(|args, _| {
max_args!(args, 2); max_args!(args, 2);
let list = match arg!(args, 0, "list") { let list = match arg!(args, 0, "list") {
Value::List(v, _) => v, Value::List(v, ..) => v,
v => vec![v], v => vec![v],
}; };
let n = match arg!(args, 1, "n") { let n = match arg!(args, 1, "n") {
@ -62,7 +62,7 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
max_args!(args, 1); max_args!(args, 1);
Ok(Value::Ident( Ok(Value::Ident(
match arg!(args, 0, "list") { match arg!(args, 0, "list") {
Value::List(_, sep) => sep.name(), Value::List(_, sep, ..) => sep.name(),
_ => ListSeparator::Space.name(), _ => ListSeparator::Space.name(),
} }
.to_owned(), .to_owned(),
@ -75,7 +75,7 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
Box::new(|args, _| { Box::new(|args, _| {
max_args!(args, 3); max_args!(args, 3);
let (mut list, sep) = match arg!(args, 0, "list") { 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), v => (vec![v], ListSeparator::Space),
}; };
let n = match arg!(args, 1, "n") { let n = match arg!(args, 1, "n") {
@ -107,7 +107,7 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
list[len - n.abs().to_integer().to_usize().unwrap()] = val; list[len - n.abs().to_integer().to_usize().unwrap()] = val;
} }
Ok(Value::List(list, sep)) Ok(Value::List(list, sep, Brackets::None))
}), }),
); );
f.insert( f.insert(
@ -115,7 +115,7 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
Box::new(|args, _| { Box::new(|args, _| {
max_args!(args, 3); max_args!(args, 3);
let (mut list, sep) = match arg!(args, 0, "list") { 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), v => (vec![v], ListSeparator::Space),
}; };
let val = arg!(args, 1, "val"); let val = arg!(args, 1, "val");
@ -137,16 +137,16 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
list.push(val); list.push(val);
Ok(Value::List(list, sep)) Ok(Value::List(list, sep, Brackets::None))
}), }),
); );
f.insert( f.insert(
"join".to_owned(), "join".to_owned(),
Box::new(|args, _| { Box::new(|args, _| {
max_args!(args, 3); max_args!(args, 3);
let (mut list1, sep1) = match arg!(args, 0, "list") { let (mut list1, sep1, brackets) = match arg!(args, 0, "list") {
Value::List(v, sep) => (v, sep), Value::List(v, sep, brackets) => (v, sep, brackets),
v => (vec![v], ListSeparator::Space), v => (vec![v], ListSeparator::Space, Brackets::None),
}; };
let list2 = match arg!(args, 1, "list") { let list2 = match arg!(args, 1, "list") {
Value::List(v, ..) => v, Value::List(v, ..) => v,
@ -176,7 +176,7 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
list1.extend(list2); list1.extend(list2);
Ok(Value::List(list1, sep)) Ok(Value::List(list1, sep, brackets))
}), }),
); );
} }

View File

@ -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)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum ListSeparator { pub(crate) enum ListSeparator {
Space, Space,

View File

@ -2,7 +2,7 @@ use std::fmt::{self, Display, Write};
use std::iter::Iterator; use std::iter::Iterator;
use crate::color::Color; use crate::color::Color;
use crate::common::{ListSeparator, Op, QuoteKind}; use crate::common::{Brackets, ListSeparator, Op, QuoteKind};
use crate::error::SassResult; use crate::error::SassResult;
use crate::unit::Unit; use crate::unit::Unit;
pub(crate) use number::Number; pub(crate) use number::Number;
@ -18,7 +18,7 @@ pub(crate) enum Value {
False, False,
Null, Null,
Dimension(Number, Unit), Dimension(Number, Unit),
List(Vec<Value>, ListSeparator), List(Vec<Value>, ListSeparator, Brackets),
Color(Color), Color(Color),
UnaryOp(Op, Box<Value>), UnaryOp(Op, Box<Value>),
BinaryOp(Box<Value>, Op, Box<Value>), BinaryOp(Box<Value>, Op, Box<Value>),
@ -39,14 +39,24 @@ impl Display for Value {
} }
_ => write!(f, "{}{}", num, unit), _ => write!(f, "{}{}", num, unit),
}, },
Self::List(vals, sep) => write!( Self::List(vals, sep, brackets) => match brackets {
f, Brackets::None => write!(
"{}", f,
vals.iter() "{}",
.map(std::string::ToString::to_string) vals.iter()
.collect::<Vec<String>>() .map(std::string::ToString::to_string)
.join(sep.as_str()) .collect::<Vec<String>>()
), .join(sep.as_str()),
),
Brackets::Bracketed => write!(
f,
"[{}]",
vals.iter()
.map(std::string::ToString::to_string)
.collect::<Vec<String>>()
.join(sep.as_str()),
),
},
Self::Color(c) => write!(f, "{}", c), Self::Color(c) => write!(f, "{}", c),
Self::UnaryOp(..) | Self::BinaryOp(..) => write!( Self::UnaryOp(..) | Self::BinaryOp(..) => write!(
f, f,

View File

@ -8,7 +8,7 @@ use num_traits::pow;
use crate::args::eat_call_args; use crate::args::eat_call_args;
use crate::builtin::GLOBAL_FUNCTIONS; use crate::builtin::GLOBAL_FUNCTIONS;
use crate::color::Color; 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::error::SassResult;
use crate::scope::Scope; use crate::scope::Scope;
use crate::selector::Selector; use crate::selector::Selector;
@ -82,19 +82,23 @@ impl Value {
None => return Ok(left), None => return Ok(left),
}; };
match next.kind { match next.kind {
TokenKind::Symbol(Symbol::SemiColon) | TokenKind::Symbol(Symbol::CloseParen) => { TokenKind::Symbol(Symbol::SemiColon)
Ok(left) | TokenKind::Symbol(Symbol::CloseParen)
} | TokenKind::Symbol(Symbol::CloseSquareBrace) => Ok(left),
TokenKind::Symbol(Symbol::Comma) => { TokenKind::Symbol(Symbol::Comma) => {
toks.next(); toks.next();
devour_whitespace_or_comment(toks); devour_whitespace_or_comment(toks);
let right = Self::from_tokens(toks, scope, super_selector)?; 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]; let mut v2 = vec![left];
v2.extend(v); v2.extend(v);
Ok(Value::List(v2, ListSeparator::Comma)) Ok(Value::List(v2, ListSeparator::Comma, Brackets::None))
} else { } else {
Ok(Value::List(vec![left, right], ListSeparator::Comma)) Ok(Value::List(
vec![left, right],
ListSeparator::Comma,
Brackets::None,
))
} }
} }
TokenKind::Symbol(Symbol::Plus) TokenKind::Symbol(Symbol::Plus)
@ -120,12 +124,16 @@ impl Value {
_ => { _ => {
devour_whitespace_or_comment(toks); devour_whitespace_or_comment(toks);
let right = Self::from_tokens(toks, scope, super_selector)?; 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]; let mut v2 = vec![left];
v2.extend(v); v2.extend(v);
Ok(Value::List(v2, ListSeparator::Space)) Ok(Value::List(v2, ListSeparator::Space, Brackets::None))
} else { } 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); devour_whitespace_or_comment(toks);
if toks.peek().unwrap().is_symbol(Symbol::CloseParen) { if toks.peek().unwrap().is_symbol(Symbol::CloseParen) {
toks.next(); 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 val = Self::from_tokens(toks, scope, super_selector)?;
let next = toks.next(); let next = toks.next();
@ -265,6 +277,15 @@ impl Value {
| q @ TokenKind::Symbol(Symbol::SingleQuote) => { | q @ TokenKind::Symbol(Symbol::SingleQuote) => {
parse_quoted_string(toks, scope, &q, super_selector) 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::Variable(ref v) => Ok(scope.get_var(v)?),
TokenKind::Interpolation => { TokenKind::Interpolation => {
let mut s = parse_interpolation(toks, scope, super_selector)?.to_string(); let mut s = parse_interpolation(toks, scope, super_selector)?.to_string();

View File

@ -140,8 +140,12 @@ test!(
"a {\n color: join((a, b), (c, d), space);\n}\n", "a {\n color: join((a, b), (c, d), space);\n}\n",
"a {\n color: a b c d;\n}\n" "a {\n color: a b c d;\n}\n"
); );
// test!( test!(
// join_bracketed, join_bracketed,
// "a {\n color: join([a], b);\n}\n", "a {\n color: join([a], b);\n}\n",
// "a {\n color: [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");