diff --git a/src/color.rs b/src/color.rs index 4244d37..2c9ebae 100644 --- a/src/color.rs +++ b/src/color.rs @@ -314,7 +314,6 @@ impl fmt::UpperHex for Color { } } - impl Display for Color { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:X}", self) @@ -477,4 +476,4 @@ impl TryFrom<&str> for Color { _ => Err("invalid color"), } } -} \ No newline at end of file +} diff --git a/src/lexer.rs b/src/lexer.rs index e077d79..6afe276 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -321,7 +321,11 @@ impl<'a> Lexer<'a> { .next() .expect("this is impossible because we have already peeked"); self.pos.next_char(); - name.push(tok); + if tok == '_' { + name.push('-'); + } else { + name.push(tok); + } } TokenKind::Variable(name) } @@ -338,7 +342,11 @@ impl<'a> Lexer<'a> { .next() .expect("this is impossible because we have already peeked"); self.pos.next_char(); - string.push(tok); + if tok == '_' { + string.push('-'); + } else { + string.push(tok); + } } if let Ok(kw) = Keyword::try_from(string.as_ref()) { diff --git a/src/main.rs b/src/main.rs index 93c0344..53afd5e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,6 +25,7 @@ clippy::let_underscore_must_use, clippy::module_name_repetitions )] +// todo! handle erroring on styles at the toplevel use std::collections::HashMap; use std::fmt::{self, Display}; use std::fs; @@ -205,7 +206,7 @@ impl<'a> StyleSheetParser<'a> { | TokenKind::Symbol(Symbol::Period) => rules.extend( self.eat_rules(&Selector(Vec::new()), &mut self.global_variables.clone()), ), - TokenKind::Whitespace(_) | TokenKind::Symbol(_) => { + TokenKind::Whitespace(_) => { self.lexer.next(); continue; } @@ -232,7 +233,13 @@ impl<'a> StyleSheetParser<'a> { rules.push(Stmt::MultilineComment(comment)); } TokenKind::AtRule(_) => self.eat_at_rule(), - _ => todo!("unexpected toplevel token"), + _ => { + if let Some(Token { pos, .. }) = self.lexer.next() { + self.error(pos.clone(), "unexpected toplevel token") + } else { + unsafe { std::hint::unreachable_unchecked() } + } + }, }; } Ok(StyleSheet { rules }) @@ -255,36 +262,31 @@ impl<'a> StyleSheetParser<'a> { .collect::(); self.error(pos, &message); } + AtRule::Warn => { + self.devour_whitespace(); + let message = self + .lexer + .by_ref() + .take_while(|x| x.kind != TokenKind::Symbol(Symbol::SemiColon)) + .map(|x| x.kind.to_string()) + .collect::(); + self.warn(pos, &message); + } + AtRule::Debug => { + self.devour_whitespace(); + let message = self + .lexer + .by_ref() + .take_while(|x| x.kind != TokenKind::Symbol(Symbol::SemiColon)) + .map(|x| x.kind.to_string()) + .collect::(); + self.debug(pos, &message); + } _ => todo!("encountered unimplemented at rule"), } } } - fn error(&self, pos: Pos, message: &str) -> ! { - eprintln!("Error: {}", message); - eprintln!( - "{} {}:{} scope on line {} at column {}", - self.file, - pos.line(), - pos.column(), - pos.line(), - pos.column() - ); - let padding = vec![' '; format!("{}", pos.line()).len() + 1] - .iter() - .collect::(); - eprintln!("{}|", padding); - eprint!("{} | ", pos.line()); - eprintln!("todo! get line to print as error"); - eprintln!( - "{}| {}^", - padding, - vec![' '; pos.column() as usize].iter().collect::() - ); - eprintln!("{}|", padding); - std::process::exit(1); - } - fn eat_variable_value(&mut self) -> Vec { self.devour_whitespace(); let iter1 = self @@ -312,6 +314,10 @@ impl<'a> StyleSheetParser<'a> { iter2 } + fn eat_func_call(&mut self) { + + } + fn eat_rules( &mut self, super_selector: &Selector, @@ -354,20 +360,28 @@ impl<'a> StyleSheetParser<'a> { super_selector: &Selector, ) -> Result { let mut values = Vec::with_capacity(5); - while let Some(tok) = self.lexer.next() { - match tok.kind { + while let Some(tok) = self.lexer.peek() { + match &tok.kind { TokenKind::Symbol(Symbol::SemiColon) | TokenKind::Symbol(Symbol::CloseBrace) => { + self.lexer.next(); self.devour_whitespace(); return Ok(Expr::Style(Style::from_tokens(&values, vars)?)); } TokenKind::Symbol(Symbol::OpenBrace) => { + self.lexer.next(); self.devour_whitespace(); return Ok(Expr::Selector(Selector::from_tokens( values.iter().peekable(), super_selector, ))); } - TokenKind::Variable(name) => { + TokenKind::Variable(_) => { + let tok = self.lexer.next().unwrap(); + let name = if let TokenKind::Variable(n) = tok.kind { + n + } else { + unsafe { std::hint::unreachable_unchecked() } + }; if let TokenKind::Symbol(Symbol::Colon) = self .lexer .peek() @@ -384,7 +398,13 @@ impl<'a> StyleSheetParser<'a> { }); } } - TokenKind::MultilineComment(ref s) => { + TokenKind::MultilineComment(_) => { + let tok = self.lexer.next().unwrap(); + let s = if let TokenKind::MultilineComment(s) = &tok.kind { + s + } else { + unsafe { std::hint::unreachable_unchecked() } + }; self.devour_whitespace(); if values.is_empty() { return Ok(Expr::MultilineComment(s.clone())); @@ -392,7 +412,14 @@ impl<'a> StyleSheetParser<'a> { values.push(tok.clone()) } } - _ => values.push(tok.clone()), + TokenKind::AtRule(_) => self.eat_at_rule(), + _ => { + if let Some(tok) = self.lexer.next() { + values.push(tok.clone()) + } else { + unsafe { std::hint::unreachable_unchecked() } + } + } }; } Err(()) @@ -410,6 +437,48 @@ impl<'a> StyleSheetParser<'a> { } } +/// Functions that print to stdout or stderr +impl<'a> StyleSheetParser<'a> { + fn debug(&self, pos: Pos, message: &str) { + println!("{}:{} Debug: {}", self.file, pos.line(), message); + } + + fn warn(&self, pos: Pos, message: &str) { + eprintln!( + "Warning: {}\n\t{} {}:{} todo!(scope)", + message, + self.file, + pos.line(), + pos.column() + ); + } + + fn error(&self, pos: Pos, message: &str) -> ! { + eprintln!("Error: {}", message); + eprintln!( + "{} {}:{} todo!(scope) on line {} at column {}", + self.file, + pos.line(), + pos.column(), + pos.line(), + pos.column() + ); + let padding = vec![' '; format!("{}", pos.line()).len() + 1] + .iter() + .collect::(); + eprintln!("{}|", padding); + eprint!("{} | ", pos.line()); + eprintln!("todo! get line to print as error"); + eprintln!( + "{}| {}^", + padding, + vec![' '; pos.column() as usize].iter().collect::() + ); + eprintln!("{}|", padding); + std::process::exit(1); + } +} + fn main() -> SassResult<()> { let mut stdout = std::io::stdout(); let s = StyleSheet::from_path("input.scss")?;