2020-01-06 16:50:16 -05:00
|
|
|
use crate::common::Symbol;
|
2020-01-12 17:44:49 -05:00
|
|
|
use crate::{Scope, Token, TokenKind};
|
2020-01-06 17:06:37 -05:00
|
|
|
use std::fmt::{self, Display};
|
2020-01-06 16:50:16 -05:00
|
|
|
use std::iter::Peekable;
|
2020-01-06 17:06:37 -05:00
|
|
|
use std::slice::Iter;
|
2020-01-06 16:50:16 -05:00
|
|
|
|
2020-01-08 20:58:02 -05:00
|
|
|
/// A style: `color: red`
|
2020-01-06 16:50:16 -05:00
|
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
|
|
pub struct Style {
|
|
|
|
property: String,
|
|
|
|
value: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Display for Style {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
2020-01-12 10:54:46 -05:00
|
|
|
write!(f, "{}: {};", self.property, self.value)
|
2020-01-06 16:50:16 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Style {
|
2020-01-12 17:44:49 -05:00
|
|
|
pub fn from_tokens(tokens: &[Token], scope: &Scope) -> Result<Self, ()> {
|
|
|
|
Ok(StyleParser::new(tokens, scope)?.parse())
|
2020-01-06 16:50:16 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct StyleParser<'a> {
|
|
|
|
tokens: Peekable<Iter<'a, Token>>,
|
2020-01-12 17:44:49 -05:00
|
|
|
scope: &'a Scope,
|
2020-01-06 16:50:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> StyleParser<'a> {
|
2020-01-12 17:44:49 -05:00
|
|
|
fn new(tokens: &'a [Token], scope: &'a Scope) -> Result<Self, ()> {
|
2020-01-06 16:50:16 -05:00
|
|
|
if tokens.is_empty() {
|
|
|
|
return Err(());
|
|
|
|
}
|
|
|
|
let tokens = tokens.iter().peekable();
|
2020-01-12 17:44:49 -05:00
|
|
|
Ok(StyleParser { tokens, scope })
|
2020-01-06 16:50:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fn deref_variable(&mut self, variable: &TokenKind) -> String {
|
|
|
|
let mut val = String::with_capacity(25);
|
|
|
|
let mut v = match variable {
|
|
|
|
TokenKind::Variable(ref v) => {
|
2020-01-12 17:44:49 -05:00
|
|
|
self.scope.vars.get(v).expect("todo! expected variable to exist")
|
2020-01-06 16:50:16 -05:00
|
|
|
}
|
|
|
|
_ => panic!("expected variable"),
|
|
|
|
}
|
|
|
|
.iter()
|
|
|
|
.peekable();
|
|
|
|
while let Some(tok) = v.next() {
|
|
|
|
match &tok.kind {
|
|
|
|
TokenKind::Variable(_) => val.push_str(&self.deref_variable(&tok.kind)),
|
|
|
|
TokenKind::Whitespace(_) => {
|
|
|
|
while let Some(w) = v.peek() {
|
|
|
|
if let TokenKind::Whitespace(_) = w.kind {
|
|
|
|
v.next();
|
|
|
|
} else {
|
|
|
|
val.push(' ');
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => val.push_str(&tok.kind.to_string()),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
val
|
|
|
|
}
|
|
|
|
|
2020-01-08 20:39:05 -05:00
|
|
|
fn devour_whitespace_or_comment(&mut self) {
|
|
|
|
while let Some(Token { kind, .. }) = self.tokens.peek() {
|
|
|
|
match kind {
|
|
|
|
TokenKind::Whitespace(_) | TokenKind::MultilineComment(_) => self.tokens.next(),
|
|
|
|
_ => break,
|
|
|
|
};
|
2020-01-06 16:50:16 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-12 10:54:46 -05:00
|
|
|
fn eat_interpolation(&mut self) -> String {
|
|
|
|
let mut val = String::new();
|
2020-01-08 20:39:05 -05:00
|
|
|
while let Some(Token { kind, .. }) = self.tokens.next() {
|
2020-01-12 10:54:46 -05:00
|
|
|
match &kind {
|
|
|
|
TokenKind::Symbol(Symbol::CloseBrace) => break,
|
|
|
|
TokenKind::Symbol(Symbol::OpenBrace) => todo!("invalid character in interpolation"),
|
|
|
|
TokenKind::Variable(_) => val.push_str(&self.deref_variable(kind)),
|
|
|
|
_ => val.push_str(&kind.to_string()),
|
|
|
|
}
|
2020-01-06 16:50:16 -05:00
|
|
|
}
|
2020-01-12 10:54:46 -05:00
|
|
|
val
|
|
|
|
}
|
2020-01-06 16:50:16 -05:00
|
|
|
|
2020-01-12 10:54:46 -05:00
|
|
|
fn parse(&mut self) -> Style {
|
|
|
|
let mut property = String::new();
|
|
|
|
// read property until `:`
|
2020-01-08 20:39:05 -05:00
|
|
|
while let Some(Token { kind, .. }) = self.tokens.next() {
|
|
|
|
match kind {
|
|
|
|
TokenKind::Whitespace(_) | TokenKind::MultilineComment(_) => continue,
|
2020-01-12 10:54:46 -05:00
|
|
|
TokenKind::Ident(ref s) => property.push_str(s),
|
|
|
|
TokenKind::Interpolation => property.push_str(&self.eat_interpolation()),
|
2020-01-06 16:50:16 -05:00
|
|
|
TokenKind::Symbol(Symbol::Colon) => break,
|
2020-01-12 10:54:46 -05:00
|
|
|
_ => property.push_str(&kind.to_string()),
|
|
|
|
};
|
2020-01-06 16:50:16 -05:00
|
|
|
}
|
|
|
|
|
2020-01-12 10:54:46 -05:00
|
|
|
self.devour_whitespace_or_comment();
|
|
|
|
|
2020-01-06 16:50:16 -05:00
|
|
|
let mut value = String::new();
|
|
|
|
|
|
|
|
// read styles
|
|
|
|
while let Some(tok) = self.tokens.next() {
|
|
|
|
match &tok.kind {
|
|
|
|
TokenKind::Whitespace(_) => {
|
2020-01-08 20:39:05 -05:00
|
|
|
while let Some(Token { kind, .. }) = self.tokens.peek() {
|
|
|
|
match kind {
|
|
|
|
TokenKind::Whitespace(_) | TokenKind::MultilineComment(_) => {
|
2020-01-06 16:50:16 -05:00
|
|
|
self.tokens.next();
|
2020-01-08 20:39:05 -05:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
TokenKind::Ident(ref s) => {
|
|
|
|
if s == &String::from("-") {
|
|
|
|
self.tokens.next();
|
|
|
|
value.push('-');
|
|
|
|
self.devour_whitespace_or_comment();
|
|
|
|
break;
|
|
|
|
}
|
2020-01-06 16:50:16 -05:00
|
|
|
}
|
2020-01-12 10:54:46 -05:00
|
|
|
TokenKind::Interpolation => {
|
|
|
|
self.tokens.next();
|
|
|
|
value.push_str(&self.eat_interpolation());
|
|
|
|
break;
|
|
|
|
}
|
2020-01-08 20:39:05 -05:00
|
|
|
_ => {}
|
2020-01-06 16:50:16 -05:00
|
|
|
}
|
|
|
|
value.push(' ');
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
TokenKind::Variable(_) => value.push_str(&self.deref_variable(&tok.kind)),
|
2020-01-08 20:39:05 -05:00
|
|
|
TokenKind::MultilineComment(_) => continue,
|
2020-01-12 10:54:46 -05:00
|
|
|
TokenKind::Interpolation => value.push_str(&self.eat_interpolation()),
|
2020-01-06 16:50:16 -05:00
|
|
|
_ => value.push_str(&tok.kind.to_string()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Style { property, value }
|
|
|
|
}
|
|
|
|
}
|