Implement basic at rule parsing and @error

This commit is contained in:
ConnorSkees 2020-01-07 18:37:28 -05:00
parent 46a6fd90f6
commit 8320c3de77
3 changed files with 156 additions and 18 deletions

View File

@ -201,6 +201,87 @@ pub enum AtRule {
CounterStyle, CounterStyle,
} }
impl TryFrom<&str> for AtRule {
type Error = &'static str;
fn try_from(c: &str) -> Result<Self, &'static str> {
match c {
"use" => Ok(AtRule::Use),
"forward" => Ok(AtRule::Forward),
"import" => Ok(AtRule::Import),
"mixin" => Ok(AtRule::Mixin),
"include" => Ok(AtRule::Include),
"function" => Ok(AtRule::Function),
"extend" => Ok(AtRule::Extend),
"atroot" => Ok(AtRule::AtRoot),
"error" => Ok(AtRule::Error),
"warn" => Ok(AtRule::Warn),
"debug" => Ok(AtRule::Debug),
"if" => Ok(AtRule::If),
"each" => Ok(AtRule::Each),
"for" => Ok(AtRule::For),
"while" => Ok(AtRule::While),
"charset" => Ok(AtRule::Charset),
"namespace" => Ok(AtRule::Namespace),
"media" => Ok(AtRule::Media),
"supports" => Ok(AtRule::Supports),
"page" => Ok(AtRule::Page),
"fontface" => Ok(AtRule::FontFace),
"keyframes" => Ok(AtRule::Keyframes),
"fontfeaturevalues" => Ok(AtRule::FontFeatureValues),
"swash" => Ok(AtRule::Swash),
"ornaments" => Ok(AtRule::Ornaments),
"annotation" => Ok(AtRule::Annotation),
"stylistic" => Ok(AtRule::Stylistic),
"styleset" => Ok(AtRule::Styleset),
"charactervariant" => Ok(AtRule::CharacterVariant),
"viewport" => Ok(AtRule::Viewport),
"document" => Ok(AtRule::Document),
"counterstyle" => Ok(AtRule::CounterStyle),
_ => Err("invalid at rule"),
}
}
}
impl Display for AtRule {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AtRule::Use => write!(f, "@use"),
AtRule::Forward => write!(f, "@forward"),
AtRule::Import => write!(f, "@import"),
AtRule::Mixin => write!(f, "@mixin"),
AtRule::Include => write!(f, "@include"),
AtRule::Function => write!(f, "@function"),
AtRule::Extend => write!(f, "@extend"),
AtRule::AtRoot => write!(f, "@atroot"),
AtRule::Error => write!(f, "@error"),
AtRule::Warn => write!(f, "@warn"),
AtRule::Debug => write!(f, "@debug"),
AtRule::If => write!(f, "@if"),
AtRule::Each => write!(f, "@each"),
AtRule::For => write!(f, "@for"),
AtRule::While => write!(f, "@while"),
AtRule::Charset => write!(f, "@charset"),
AtRule::Namespace => write!(f, "@namespace"),
AtRule::Media => write!(f, "@media"),
AtRule::Supports => write!(f, "@supports"),
AtRule::Page => write!(f, "@page"),
AtRule::FontFace => write!(f, "@fontface"),
AtRule::Keyframes => write!(f, "@keyframes"),
AtRule::FontFeatureValues => write!(f, "@fontfeaturevalues"),
AtRule::Swash => write!(f, "@swash"),
AtRule::Ornaments => write!(f, "@ornaments"),
AtRule::Annotation => write!(f, "@annotation"),
AtRule::Stylistic => write!(f, "@stylistic"),
AtRule::Styleset => write!(f, "@styleset"),
AtRule::CharacterVariant => write!(f, "@charactervariant"),
AtRule::Viewport => write!(f, "@viewport"),
AtRule::Document => write!(f, "@document"),
AtRule::CounterStyle => write!(f, "@counterstyle"),
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)] #[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Whitespace { pub enum Whitespace {
Space, Space,
@ -268,6 +349,7 @@ pub enum Keyword {
And, And,
Or, Or,
Null, Null,
In,
} }
impl Display for Keyword { impl Display for Keyword {
@ -286,6 +368,7 @@ impl Display for Keyword {
Keyword::And => write!(f, "and"), Keyword::And => write!(f, "and"),
Keyword::Or => write!(f, "or"), Keyword::Or => write!(f, "or"),
Keyword::Null => write!(f, "null"), Keyword::Null => write!(f, "null"),
Keyword::In => write!(f, "in"),
} }
} }
} }
@ -306,6 +389,7 @@ impl Into<&'static str> for Keyword {
Keyword::And => "and", Keyword::And => "and",
Keyword::Or => "or", Keyword::Or => "or",
Keyword::Null => "null", Keyword::Null => "null",
Keyword::In => "in",
} }
} }
} }
@ -329,6 +413,7 @@ impl TryFrom<&str> for Keyword {
"and" => Ok(Keyword::And), "and" => Ok(Keyword::And),
"or" => Ok(Keyword::Or), "or" => Ok(Keyword::Or),
"null" => Ok(Keyword::Null), "null" => Ok(Keyword::Null),
"in" => Ok(Keyword::In),
_ => Err("invalid keyword"), _ => Err("invalid keyword"),
} }
} }
@ -653,7 +738,7 @@ pub struct Pos {
impl Pos { impl Pos {
pub const fn new() -> Self { pub const fn new() -> Self {
Pos { line: 0, column: 0 } Pos { line: 1, column: 1 }
} }
pub const fn line(self) -> u32 { pub const fn line(self) -> u32 {

View File

@ -2,7 +2,7 @@ use std::convert::TryFrom;
use std::iter::Peekable; use std::iter::Peekable;
use std::str::Chars; use std::str::Chars;
use crate::common::{Keyword, Op, Pos, Symbol}; use crate::common::{AtRule, Keyword, Op, Pos, Symbol};
use crate::selector::{Attribute, AttributeKind, Selector}; use crate::selector::{Attribute, AttributeKind, Selector};
use crate::units::Unit; use crate::units::Unit;
use crate::{Token, TokenKind, Whitespace}; use crate::{Token, TokenKind, Whitespace};
@ -46,8 +46,15 @@ impl<'a> Iterator for Lexer<'a> {
'"' => symbol!(self, DoubleQuote), '"' => symbol!(self, DoubleQuote),
' ' => whitespace!(self, Space), ' ' => whitespace!(self, Space),
'\t' => whitespace!(self, Tab), '\t' => whitespace!(self, Tab),
'\n' => whitespace!(self, Newline), '\n' => {
'\r' => whitespace!(self, CarriageReturn), self.buf.next();
self.pos.newline();
TokenKind::Whitespace(Whitespace::Newline)
}
'\r' => {
self.buf.next();
TokenKind::Whitespace(Whitespace::CarriageReturn)
}
'#' => symbol!(self, Hash), '#' => symbol!(self, Hash),
'{' => symbol!(self, OpenBrace), '{' => symbol!(self, OpenBrace),
'*' => symbol!(self, Mul), '*' => symbol!(self, Mul),
@ -125,6 +132,7 @@ impl<'a> Lexer<'a> {
} }
fn lex_at_rule(&mut self) -> TokenKind { fn lex_at_rule(&mut self) -> TokenKind {
self.buf.next();
let mut string = String::with_capacity(99); let mut string = String::with_capacity(99);
while let Some(c) = self.buf.peek() { while let Some(c) = self.buf.peek() {
if !c.is_alphabetic() && c != &'-' && c != &'_' { if !c.is_alphabetic() && c != &'-' && c != &'_' {
@ -138,8 +146,8 @@ impl<'a> Lexer<'a> {
string.push(tok); string.push(tok);
} }
if let Ok(kw) = Unit::try_from(string.as_ref()) { if let Ok(rule) = AtRule::try_from(string.as_ref()) {
TokenKind::Unit(kw) TokenKind::AtRule(rule)
} else { } else {
panic!("expected ident after `@`") panic!("expected ident after `@`")
} }

View File

@ -29,7 +29,7 @@ use std::fs;
use std::io; use std::io;
use std::iter::{Iterator, Peekable}; use std::iter::{Iterator, Peekable};
use crate::common::{Keyword, Op, Pos, Symbol, Whitespace}; use crate::common::{AtRule, Keyword, Op, Pos, Symbol, Whitespace};
use crate::css::Css; use crate::css::Css;
use crate::error::SassError; use crate::error::SassError;
use crate::format::PrettyPrinter; use crate::format::PrettyPrinter;
@ -55,7 +55,7 @@ pub enum TokenKind {
Ident(String), Ident(String),
Symbol(Symbol), Symbol(Symbol),
Function(String, Vec<String>), Function(String, Vec<String>),
AtRule(String), AtRule(AtRule),
Keyword(Keyword), Keyword(Keyword),
Number(String), Number(String),
Unit(Unit), Unit(Unit),
@ -71,8 +71,9 @@ pub enum TokenKind {
impl Display for TokenKind { impl Display for TokenKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
TokenKind::Ident(s) | TokenKind::Number(s) | TokenKind::AtRule(s) => write!(f, "{}", s), TokenKind::Ident(s) | TokenKind::Number(s) => write!(f, "{}", s),
TokenKind::Symbol(s) => write!(f, "{}", s), TokenKind::Symbol(s) => write!(f, "{}", s),
TokenKind::AtRule(s) => write!(f, "{}", s),
TokenKind::Op(s) => write!(f, "{}", s), TokenKind::Op(s) => write!(f, "{}", s),
TokenKind::Unit(s) => write!(f, "{}", s), TokenKind::Unit(s) => write!(f, "{}", s),
TokenKind::Whitespace(s) => write!(f, "{}", s), TokenKind::Whitespace(s) => write!(f, "{}", s),
@ -119,7 +120,6 @@ enum Expr {
} }
impl StyleSheet { impl StyleSheet {
#[must_use]
pub fn new(input: &str) -> SassResult<StyleSheet> { pub fn new(input: &str) -> SassResult<StyleSheet> {
StyleSheetParser { StyleSheetParser {
global_variables: HashMap::new(), global_variables: HashMap::new(),
@ -154,8 +154,8 @@ struct StyleSheetParser<'a> {
impl<'a> StyleSheetParser<'a> { impl<'a> StyleSheetParser<'a> {
fn parse_toplevel(&mut self) -> SassResult<StyleSheet> { fn parse_toplevel(&mut self) -> SassResult<StyleSheet> {
let mut rules = Vec::new(); let mut rules = Vec::new();
while let Some(tok) = self.lexer.peek() { while let Some(Token { kind, .. }) = self.lexer.peek() {
match tok.kind.clone() { match kind.clone() {
TokenKind::Ident(_) TokenKind::Ident(_)
| TokenKind::Selector(_) | TokenKind::Selector(_)
| TokenKind::Symbol(Symbol::Hash) | TokenKind::Symbol(Symbol::Hash)
@ -168,27 +168,69 @@ impl<'a> StyleSheetParser<'a> {
continue; continue;
} }
TokenKind::Variable(name) => { TokenKind::Variable(name) => {
self.lexer.next(); let Token { pos, .. } = self
self.devour_whitespace();
if self
.lexer .lexer
.next() .next()
.expect("expected something after variable") .expect("this cannot occur as we have already peeked");
// .unwrap_or(Err(SassError::new("expected value after variable", this_tok.pos))?) self.devour_whitespace();
if self
.lexer
.next()
.unwrap_or_else(|| self.error(pos, "expected value after variable"))
.kind .kind
!= TokenKind::Symbol(Symbol::Colon) != TokenKind::Symbol(Symbol::Colon)
{ {
panic!("unexpected variable use at toplevel") self.error(pos, "unexpected variable use at toplevel");
} }
let val = self.eat_variable_value(); let val = self.eat_variable_value();
self.global_variables.insert(name, val); self.global_variables.insert(name, val);
} }
TokenKind::AtRule(_) => self.eat_at_rule(),
_ => todo!("unexpected toplevel token"), _ => todo!("unexpected toplevel token"),
}; };
} }
Ok(StyleSheet { rules }) Ok(StyleSheet { rules })
} }
fn eat_at_rule(&mut self) {
if let Some(Token {
kind: TokenKind::AtRule(rule),
pos,
}) = self.lexer.next()
{
match rule {
AtRule::Error => {
let message = self
.lexer
.by_ref()
.take_while(|x| x.kind != TokenKind::Symbol(Symbol::SemiColon))
.map(|x| x.kind.to_string())
.collect::<String>();
self.error(pos, &message);
}
_ => todo!("encountered unimplemented at rule"),
}
}
}
fn error(&mut self, pos: Pos, message: &str) -> ! {
eprintln!("Error: {}", message);
eprintln!(
"filename {}:{} scope on line {} at column {}",
pos.line(),
pos.column(),
pos.line(),
pos.column()
);
let padding = vec![' '; format!("{}", pos.line()).len() + 1].iter().collect::<String>();
eprintln!("{}|", padding);
eprint!("{} | ", pos.line());
eprintln!("placeholder!");
eprintln!("{}| {}^", padding, vec![' '; pos.column() as usize].iter().collect::<String>());
eprintln!("{}|", padding);
std::process::exit(1);
}
fn eat_variable_value(&mut self) -> Vec<Token> { fn eat_variable_value(&mut self) -> Vec<Token> {
self.devour_whitespace(); self.devour_whitespace();
let iter1 = self let iter1 = self
@ -230,6 +272,9 @@ impl<'a> StyleSheetParser<'a> {
rules, rules,
})); }));
self.scope -= 1; self.scope -= 1;
if self.scope == 0 {
return stmts;
}
} }
Expr::VariableDecl(name, val) => { Expr::VariableDecl(name, val) => {
if self.scope == 0 { if self.scope == 0 {