Implement basic variable handling

This commit is contained in:
ConnorSkees 2020-01-05 19:21:58 -05:00
parent 85ea52030b
commit 874c36a959

View File

@ -61,6 +61,25 @@ pub enum TokenKind {
Variable(String), Variable(String),
Selector(Selector), Selector(Selector),
Style(Vec<Token>), Style(Vec<Token>),
// todo! preserve multi-line comments
MultilineComment(String),
}
impl Display for TokenKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TokenKind::Ident(s) | TokenKind::Number(s) | TokenKind::AtRule(s) => write!(f, "{}", s),
TokenKind::Symbol(s) => write!(f, "{}", s),
TokenKind::Unit(s) => write!(f, "{}", s),
TokenKind::Whitespace(s) => write!(f, "{}", s),
TokenKind::Selector(s) => write!(f, "{}", s),
TokenKind::Function(name, args) => write!(f, "{}({})", name, args.join(", ")),
TokenKind::Keyword(kw) => write!(f, "{}", kw),
TokenKind::MultilineComment(s) => write!(f, "/*{}*/", s),
TokenKind::Variable(s) => write!(f, "${}", s),
TokenKind::Style(_) => panic!("TokenKind should not be used to format styles"),
}
}
} }
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
@ -116,8 +135,28 @@ impl Display for Style {
} }
} }
struct StyleParser<'a> {
tokens: &'a [Token],
vars: &'a HashMap<String, Vec<Token>>,
}
impl Style { impl Style {
fn from_tokens(raw: &[Token]) -> Result<Style, ()> { fn deref_variable(variable: &TokenKind, vars: &HashMap<String, Vec<Token>>) -> String {
let mut val = String::with_capacity(15);
let v = match variable {
TokenKind::Variable(ref v) => vars.get(v).expect("todo! expected variable to exist"),
_ => panic!("expected variable"),
};
for tok in v {
match &tok.kind {
TokenKind::Variable(_) => val.push_str(&Self::deref_variable(&tok.kind, vars)),
_ => val.push_str(&tok.kind.to_string()),
};
}
val
}
fn from_tokens(raw: &[Token], vars: &HashMap<String, Vec<Token>>) -> Result<Style, ()> {
let mut iter = raw.iter(); let mut iter = raw.iter();
let property: String; let property: String;
loop { loop {
@ -153,6 +192,9 @@ impl Style {
TokenKind::Ident(ref s) => value.push(StyleToken::Ident(s.clone())), TokenKind::Ident(ref s) => value.push(StyleToken::Ident(s.clone())),
TokenKind::Symbol(s) => value.push(StyleToken::Symbol(s)), TokenKind::Symbol(s) => value.push(StyleToken::Symbol(s)),
TokenKind::Unit(u) => value.push(StyleToken::Ident(u.into())), TokenKind::Unit(u) => value.push(StyleToken::Ident(u.into())),
TokenKind::Variable(_) => {
value.push(StyleToken::Ident(Self::deref_variable(&tok.kind, vars)))
}
TokenKind::Number(ref num) => { TokenKind::Number(ref num) => {
if let Some(t) = iter.next() { if let Some(t) = iter.next() {
match &t.kind { match &t.kind {
@ -197,13 +239,14 @@ pub struct RuleSet {
enum Expr { enum Expr {
Style(Style), Style(Style),
Selector(Selector), Selector(Selector),
VariableDecl(String, Vec<Token>),
} }
impl StyleSheet { impl StyleSheet {
#[must_use] #[must_use]
pub fn new(input: &str) -> StyleSheet { pub fn new(input: &str) -> StyleSheet {
StyleSheetParser { StyleSheetParser {
variables: HashMap::new(), global_variables: HashMap::new(),
lexer: Lexer::new(input).peekable(), lexer: Lexer::new(input).peekable(),
rules: Vec::new(), rules: Vec::new(),
} }
@ -225,59 +268,89 @@ impl StyleSheet {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct StyleSheetParser<'a> { struct StyleSheetParser<'a> {
variables: HashMap<String, String>, global_variables: HashMap<String, Vec<Token>>,
lexer: Peekable<Lexer<'a>>, lexer: Peekable<Lexer<'a>>,
rules: Vec<Stmt>, rules: Vec<Stmt>,
} }
impl<'a> StyleSheetParser<'a> { impl<'a> StyleSheetParser<'a> {
fn parse_toplevel(&'a mut self) -> StyleSheet { fn parse_toplevel(&mut self) -> StyleSheet {
let mut rules = Vec::new(); let mut rules = Vec::new();
while let Some(tok) = self.lexer.peek() { while let Some(tok) = self.lexer.peek() {
match tok.kind { match tok.kind.clone() {
TokenKind::Ident(_) TokenKind::Ident(_)
| TokenKind::Selector(_) | TokenKind::Selector(_)
| TokenKind::Symbol(Symbol::Hash) | TokenKind::Symbol(Symbol::Hash)
| TokenKind::Symbol(Symbol::Colon) | TokenKind::Symbol(Symbol::Colon)
| TokenKind::Symbol(Symbol::Mul) | TokenKind::Symbol(Symbol::Mul)
| TokenKind::Symbol(Symbol::Period) => { | TokenKind::Symbol(Symbol::Period) => rules
rules.extend(self.eat_rules(&Selector::None)) .extend(self.eat_rules(&Selector::None, &mut self.global_variables.clone())),
}
TokenKind::Whitespace(_) | TokenKind::Symbol(_) => { TokenKind::Whitespace(_) | TokenKind::Symbol(_) => {
self.lexer.next(); self.lexer.next();
continue; continue;
} }
TokenKind::Variable(name) => {
self.lexer.next();
self.devour_whitespace();
if self
.lexer
.next()
.expect("expected something after variable")
.kind
!= TokenKind::Symbol(Symbol::Colon)
{
panic!("unexpected variable use at toplevel")
}
let val = self.eat_variable_value();
self.global_variables.insert(name, val);
}
_ => todo!("unexpected toplevel token"), _ => todo!("unexpected toplevel token"),
}; };
} }
StyleSheet { rules } StyleSheet { rules }
} }
fn eat_rules(&mut self, super_selector: &Selector) -> Vec<Stmt> { fn eat_variable_value(&mut self) -> Vec<Token> {
self.devour_whitespace();
self.lexer
.by_ref()
.take_while(|x| x.kind != TokenKind::Symbol(Symbol::SemiColon))
.collect::<Vec<Token>>()
}
fn eat_rules(
&mut self,
super_selector: &Selector,
vars: &mut HashMap<String, Vec<Token>>,
) -> Vec<Stmt> {
let mut stmts = Vec::new(); let mut stmts = Vec::new();
while let Ok(tok) = self.eat_expr() { while let Ok(tok) = self.eat_expr(vars) {
match tok { match tok {
Expr::Style(s) => stmts.push(Stmt::Style(s)), Expr::Style(s) => stmts.push(Stmt::Style(s)),
Expr::Selector(s) => { Expr::Selector(s) => {
let rules = self.eat_rules(&super_selector.clone().zip(s.clone())); let rules = self.eat_rules(&super_selector.clone().zip(s.clone()), vars);
stmts.push(Stmt::RuleSet(RuleSet { stmts.push(Stmt::RuleSet(RuleSet {
super_selector: super_selector.clone(), super_selector: super_selector.clone(),
selector: s, selector: s,
rules, rules,
})); }));
} }
Expr::VariableDecl(name, val) => {
vars.insert(name, val);
}
} }
} }
stmts stmts
} }
fn eat_expr(&mut self) -> Result<Expr, ()> { fn eat_expr(&mut self, vars: &HashMap<String, Vec<Token>>) -> Result<Expr, ()> {
let mut values = Vec::with_capacity(5); let mut values = Vec::with_capacity(5);
while let Some(tok) = self.lexer.next() { while let Some(tok) = self.lexer.next() {
dbg!(&tok.kind);
match tok.kind { match tok.kind {
TokenKind::Symbol(Symbol::SemiColon) | TokenKind::Symbol(Symbol::CloseBrace) => { TokenKind::Symbol(Symbol::SemiColon) | TokenKind::Symbol(Symbol::CloseBrace) => {
self.devour_whitespace(); self.devour_whitespace();
return Ok(Expr::Style(Style::from_tokens(&values)?)); return Ok(Expr::Style(Style::from_tokens(&values, vars)?));
} }
TokenKind::Symbol(Symbol::OpenBrace) => { TokenKind::Symbol(Symbol::OpenBrace) => {
self.devour_whitespace(); self.devour_whitespace();
@ -285,6 +358,23 @@ impl<'a> StyleSheetParser<'a> {
values.iter().peekable(), values.iter().peekable(),
))); )));
} }
TokenKind::Variable(name) => {
if self
.lexer
.next()
.expect("expected something after variable")
.kind
== TokenKind::Symbol(Symbol::Colon)
{
self.devour_whitespace();
return Ok(Expr::VariableDecl(name, self.eat_variable_value()));
} else {
values.push(Token {
kind: TokenKind::Variable(name),
pos: tok.pos,
});
}
}
_ => values.push(tok.clone()), _ => values.push(tok.clone()),
}; };
} }