Initial implementation of Value
This commit is contained in:
parent
4fe74ecf51
commit
9503b3288b
22
src/lib.rs
22
src/lib.rs
@ -71,6 +71,7 @@ mod selector;
|
||||
mod style;
|
||||
mod units;
|
||||
mod utils;
|
||||
mod value;
|
||||
|
||||
pub type SassResult<T> = Result<T, SassError>;
|
||||
|
||||
@ -1291,3 +1292,24 @@ mod test_imports {
|
||||
test_import!(finds_name_scss, "@import \"finds_name_scss\";\na {\n color: $a;\n}" => "a {\n color: red;\n}\n" | "finds_name_scss.scss"("$a: red;"));
|
||||
test_import!(finds_underscore_name_scss, "@import \"finds_underscore_name_scss\";\na {\n color: $a;\n}" => "a {\n color: red;\n}\n" | "_finds_underscore_name_scss.scss"("$a: red;"));
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_values {
|
||||
use super::*;
|
||||
test!(comma_list_ident, "a {\n color: red, white, blue;\n}\n");
|
||||
test!(space_list_ident, "a {\n color: red white blue;\n}\n");
|
||||
test!(comma_list_number, "a {\n color: 1, 2, 3;\n}\n");
|
||||
test!(space_list_number, "a {\n color: 1 2 3;\n}\n");
|
||||
test!(comma_space_list_number, "a {\n color: 1 1, 2 2, 3 3;\n}\n");
|
||||
test!(
|
||||
whitespace_space_list_number,
|
||||
"a {\n color: 1 2 3 ;\n}\n",
|
||||
"a {\n color: 1 2 3;\n}\n"
|
||||
);
|
||||
test!(
|
||||
whitespace_comma_list_number,
|
||||
"a {\n color: 1 , 2 , 3 ;\n}\n",
|
||||
"a {\n color: 1, 2, 3;\n}\n"
|
||||
);
|
||||
test!(number, "a {\n color: 1;\n}\n");
|
||||
}
|
||||
|
54
src/style.rs
54
src/style.rs
@ -10,7 +10,7 @@ use std::vec::IntoIter;
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub(crate) struct Style {
|
||||
property: String,
|
||||
value: String,
|
||||
value: Value,
|
||||
}
|
||||
|
||||
impl Display for Style {
|
||||
@ -59,58 +59,8 @@ impl<'a> StyleParser<'a> {
|
||||
|
||||
devour_whitespace_or_comment(&mut self.tokens);
|
||||
|
||||
let mut value = String::new();
|
||||
let value = Value::from_tokens(&mut self.tokens, self.scope).unwrap();
|
||||
|
||||
// read styles
|
||||
while let Some(tok) = self.tokens.next() {
|
||||
match &tok.kind {
|
||||
TokenKind::Whitespace(_) => {
|
||||
while let Some(Token { kind, .. }) = self.tokens.peek() {
|
||||
match kind {
|
||||
TokenKind::Whitespace(_) | TokenKind::MultilineComment(_) => {
|
||||
self.tokens.next();
|
||||
continue;
|
||||
}
|
||||
TokenKind::Ident(ref s) => {
|
||||
if s == &String::from("-") {
|
||||
self.tokens.next();
|
||||
value.push('-');
|
||||
self.devour_whitespace_or_comment();
|
||||
break;
|
||||
}
|
||||
}
|
||||
TokenKind::Interpolation => {
|
||||
self.tokens.next();
|
||||
value.push_str(
|
||||
&eat_interpolation(&mut self.tokens, self.scope)
|
||||
.iter()
|
||||
.map(|x| x.kind.to_string())
|
||||
.collect::<String>(),
|
||||
);
|
||||
break;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
value.push(' ');
|
||||
break;
|
||||
}
|
||||
}
|
||||
TokenKind::Variable(ref v) => value.push_str(
|
||||
&deref_variable(v, self.scope)
|
||||
.iter()
|
||||
.map(|x| x.kind.to_string())
|
||||
.collect::<String>(),
|
||||
),
|
||||
TokenKind::MultilineComment(_) => continue,
|
||||
TokenKind::Interpolation => value.push_str(
|
||||
&eat_interpolation(&mut self.tokens, self.scope)
|
||||
.iter()
|
||||
.map(|x| x.kind.to_string())
|
||||
.collect::<String>(),
|
||||
),
|
||||
_ => value.push_str(&tok.kind.to_string()),
|
||||
}
|
||||
}
|
||||
Style { property, value }
|
||||
}
|
||||
}
|
||||
|
300
src/value.rs
Normal file
300
src/value.rs
Normal file
@ -0,0 +1,300 @@
|
||||
#![allow(dead_code, unused_variables)]
|
||||
use std::fmt::{self, Display};
|
||||
use std::iter::{Iterator, Peekable};
|
||||
use std::ops::{Add, Div, Mul, Rem, Sub};
|
||||
|
||||
use crate::color::Color;
|
||||
use crate::common::{Keyword, Op, Scope, Symbol};
|
||||
use crate::units::Unit;
|
||||
use crate::utils::{deref_variable, devour_whitespace_or_comment, eat_interpolation};
|
||||
use crate::{Token, TokenKind};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub(crate) struct Dimension {
|
||||
val: u64,
|
||||
}
|
||||
|
||||
impl Add for Dimension {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, other: Self) -> Self {
|
||||
Dimension {
|
||||
val: self.val + other.val,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Dimension {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.val)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub(crate) enum ListSeparator {
|
||||
Space,
|
||||
Comma,
|
||||
}
|
||||
|
||||
impl ListSeparator {
|
||||
pub fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::Space => " ",
|
||||
Self::Comma => ", ",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(self) -> &'static str {
|
||||
match self {
|
||||
Self::Space => "space",
|
||||
Self::Comma => "comma",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ListSeparator {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Space => write!(f, " "),
|
||||
Self::Comma => write!(f, ", "),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub(crate) enum QuoteKind {
|
||||
Single,
|
||||
Double,
|
||||
None,
|
||||
}
|
||||
|
||||
impl QuoteKind {
|
||||
pub fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::Single => "'",
|
||||
Self::Double => "\"",
|
||||
Self::None => "",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub(crate) enum Value {
|
||||
Important,
|
||||
True,
|
||||
False,
|
||||
Null,
|
||||
Dimension(Dimension, Unit),
|
||||
List(Vec<Value>, ListSeparator),
|
||||
Color(Color),
|
||||
Paren(Box<Value>),
|
||||
Ident(String, QuoteKind),
|
||||
}
|
||||
|
||||
impl Add for Value {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, other: Self) -> Self {
|
||||
match self {
|
||||
Self::Important => todo!(),
|
||||
Self::True => todo!(),
|
||||
Self::False => todo!(),
|
||||
Self::Null => todo!(),
|
||||
Self::Dimension(num, unit) => match other {
|
||||
Self::Dimension(num2, unit2) => Value::Dimension(num + num2, unit),
|
||||
_ => todo!(),
|
||||
},
|
||||
Self::List(..) => todo!(),
|
||||
Self::Color(..) => todo!(),
|
||||
Self::Paren(..) => todo!(),
|
||||
Self::Ident(..) => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Value {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Important => write!(f, "!important"),
|
||||
Self::Dimension(num, unit) => write!(f, "{}{}", num, unit),
|
||||
Self::List(vals, sep) => write!(
|
||||
f,
|
||||
"{}",
|
||||
vals.iter()
|
||||
.map(|x| x.to_string())
|
||||
.collect::<Vec<String>>()
|
||||
.join(sep.as_str())
|
||||
),
|
||||
Self::Color(c) => write!(f, "{}", c),
|
||||
Self::Paren(val) => write!(f, "({})", val),
|
||||
Self::Ident(val, kind) => write!(f, "{}{}{}", kind.as_str(), val, kind.as_str()),
|
||||
Self::True => write!(f, "true"),
|
||||
Self::False => write!(f, "false"),
|
||||
Self::Null => write!(f, "null"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Value {
|
||||
pub fn is_true(&self) -> bool {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn unquote(&mut self) -> &mut Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn from_tokens<I: Iterator<Item = Token>>(
|
||||
toks: &mut Peekable<I>,
|
||||
scope: &Scope,
|
||||
) -> Option<Self> {
|
||||
let left = Self::_from_tokens(toks, scope)?;
|
||||
let whitespace = devour_whitespace_or_comment(toks);
|
||||
let next = match toks.peek() {
|
||||
Some(x) => x,
|
||||
None => return Some(left),
|
||||
};
|
||||
match next.kind {
|
||||
TokenKind::Symbol(Symbol::Comma) => {
|
||||
toks.next();
|
||||
devour_whitespace_or_comment(toks);
|
||||
let right = match Self::from_tokens(toks, scope) {
|
||||
Some(x) => x,
|
||||
None => return Some(left),
|
||||
};
|
||||
Some(Value::List(vec![left, right], ListSeparator::Comma))
|
||||
}
|
||||
TokenKind::Symbol(Symbol::Plus) => {
|
||||
toks.next();
|
||||
devour_whitespace_or_comment(toks);
|
||||
let right = match Self::from_tokens(toks, scope) {
|
||||
Some(x) => x,
|
||||
None => return Some(left),
|
||||
};
|
||||
Some(left + right)
|
||||
}
|
||||
_ if whitespace => {
|
||||
devour_whitespace_or_comment(toks);
|
||||
let right = match Self::from_tokens(toks, scope) {
|
||||
Some(x) => x,
|
||||
None => return Some(left),
|
||||
};
|
||||
Some(Value::List(vec![left, right], ListSeparator::Space))
|
||||
}
|
||||
_ => {
|
||||
dbg!(&next.kind);
|
||||
todo!("unimplemented token in value")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn _from_tokens<I: Iterator<Item = Token>>(
|
||||
toks: &mut Peekable<I>,
|
||||
scope: &Scope,
|
||||
) -> Option<Self> {
|
||||
let kind = if let Some(tok) = toks.next() {
|
||||
tok.kind
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
match kind {
|
||||
TokenKind::Number(val) => {
|
||||
let unit = if let Some(tok) = toks.peek() {
|
||||
match tok.kind.clone() {
|
||||
TokenKind::Ident(i) => {
|
||||
toks.next();
|
||||
Unit::from(&i)
|
||||
}
|
||||
TokenKind::Symbol(Symbol::Percent) => {
|
||||
toks.next();
|
||||
Unit::Percent
|
||||
}
|
||||
_ => Unit::None,
|
||||
}
|
||||
} else {
|
||||
Unit::None
|
||||
};
|
||||
Some(Value::Dimension(
|
||||
Dimension {
|
||||
val: val.parse().unwrap(),
|
||||
},
|
||||
unit,
|
||||
))
|
||||
}
|
||||
TokenKind::Ident(mut s) => {
|
||||
while let Some(tok) = toks.peek() {
|
||||
match tok.kind.clone() {
|
||||
TokenKind::Interpolation => {
|
||||
toks.next();
|
||||
s.push_str(
|
||||
&eat_interpolation(toks, scope)
|
||||
.iter()
|
||||
.map(|x| x.kind.to_string())
|
||||
.collect::<String>(),
|
||||
)
|
||||
}
|
||||
TokenKind::Ident(ref i) => {
|
||||
toks.next();
|
||||
s.push_str(i)
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
Some(Value::Ident(s, QuoteKind::None))
|
||||
}
|
||||
TokenKind::Symbol(Symbol::DoubleQuote) => {
|
||||
let mut s = String::new();
|
||||
while let Some(tok) = toks.next() {
|
||||
match tok.kind {
|
||||
TokenKind::Symbol(Symbol::DoubleQuote) => break,
|
||||
_ => {}
|
||||
}
|
||||
s.push_str(&tok.kind.to_string());
|
||||
}
|
||||
Some(Value::Ident(s, QuoteKind::Double))
|
||||
}
|
||||
TokenKind::Symbol(Symbol::SingleQuote) => {
|
||||
let mut s = String::new();
|
||||
while let Some(tok) = toks.next() {
|
||||
match tok.kind {
|
||||
TokenKind::Symbol(Symbol::SingleQuote) => break,
|
||||
_ => {}
|
||||
}
|
||||
s.push_str(&tok.kind.to_string());
|
||||
}
|
||||
Some(Value::Ident(s, QuoteKind::Single))
|
||||
}
|
||||
TokenKind::Variable(ref v) => {
|
||||
Value::from_tokens(&mut deref_variable(v, scope).into_iter().peekable(), scope)
|
||||
}
|
||||
TokenKind::Interpolation => {
|
||||
let mut s = eat_interpolation(toks, scope)
|
||||
.iter()
|
||||
.map(|x| x.kind.to_string())
|
||||
.collect::<String>();
|
||||
while let Some(tok) = toks.peek() {
|
||||
match tok.kind.clone() {
|
||||
TokenKind::Interpolation => {
|
||||
toks.next();
|
||||
s.push_str(
|
||||
&eat_interpolation(toks, scope)
|
||||
.iter()
|
||||
.map(|x| x.kind.to_string())
|
||||
.collect::<String>(),
|
||||
)
|
||||
}
|
||||
TokenKind::Ident(ref i) => {
|
||||
toks.next();
|
||||
s.push_str(i)
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
Some(Value::Ident(s, QuoteKind::None))
|
||||
}
|
||||
TokenKind::Keyword(Keyword::Important) => Some(Value::Important),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user