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,
}
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)]
pub enum Whitespace {
Space,
@ -268,6 +349,7 @@ pub enum Keyword {
And,
Or,
Null,
In,
}
impl Display for Keyword {
@ -286,6 +368,7 @@ impl Display for Keyword {
Keyword::And => write!(f, "and"),
Keyword::Or => write!(f, "or"),
Keyword::Null => write!(f, "null"),
Keyword::In => write!(f, "in"),
}
}
}
@ -306,6 +389,7 @@ impl Into<&'static str> for Keyword {
Keyword::And => "and",
Keyword::Or => "or",
Keyword::Null => "null",
Keyword::In => "in",
}
}
}
@ -329,6 +413,7 @@ impl TryFrom<&str> for Keyword {
"and" => Ok(Keyword::And),
"or" => Ok(Keyword::Or),
"null" => Ok(Keyword::Null),
"in" => Ok(Keyword::In),
_ => Err("invalid keyword"),
}
}
@ -653,7 +738,7 @@ pub struct Pos {
impl Pos {
pub const fn new() -> Self {
Pos { line: 0, column: 0 }
Pos { line: 1, column: 1 }
}
pub const fn line(self) -> u32 {

View File

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

View File

@ -29,7 +29,7 @@ use std::fs;
use std::io;
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::error::SassError;
use crate::format::PrettyPrinter;
@ -55,7 +55,7 @@ pub enum TokenKind {
Ident(String),
Symbol(Symbol),
Function(String, Vec<String>),
AtRule(String),
AtRule(AtRule),
Keyword(Keyword),
Number(String),
Unit(Unit),
@ -71,8 +71,9 @@ pub enum TokenKind {
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::Ident(s) | TokenKind::Number(s) => write!(f, "{}", s),
TokenKind::Symbol(s) => write!(f, "{}", s),
TokenKind::AtRule(s) => write!(f, "{}", s),
TokenKind::Op(s) => write!(f, "{}", s),
TokenKind::Unit(s) => write!(f, "{}", s),
TokenKind::Whitespace(s) => write!(f, "{}", s),
@ -119,7 +120,6 @@ enum Expr {
}
impl StyleSheet {
#[must_use]
pub fn new(input: &str) -> SassResult<StyleSheet> {
StyleSheetParser {
global_variables: HashMap::new(),
@ -154,8 +154,8 @@ struct StyleSheetParser<'a> {
impl<'a> StyleSheetParser<'a> {
fn parse_toplevel(&mut self) -> SassResult<StyleSheet> {
let mut rules = Vec::new();
while let Some(tok) = self.lexer.peek() {
match tok.kind.clone() {
while let Some(Token { kind, .. }) = self.lexer.peek() {
match kind.clone() {
TokenKind::Ident(_)
| TokenKind::Selector(_)
| TokenKind::Symbol(Symbol::Hash)
@ -168,27 +168,69 @@ impl<'a> StyleSheetParser<'a> {
continue;
}
TokenKind::Variable(name) => {
self.lexer.next();
self.devour_whitespace();
if self
let Token { pos, .. } = self
.lexer
.next()
.expect("expected something after variable")
// .unwrap_or(Err(SassError::new("expected value after variable", this_tok.pos))?)
.expect("this cannot occur as we have already peeked");
self.devour_whitespace();
if self
.lexer
.next()
.unwrap_or_else(|| self.error(pos, "expected value after variable"))
.kind
!= TokenKind::Symbol(Symbol::Colon)
{
panic!("unexpected variable use at toplevel")
self.error(pos, "unexpected variable use at toplevel");
}
let val = self.eat_variable_value();
self.global_variables.insert(name, val);
}
TokenKind::AtRule(_) => self.eat_at_rule(),
_ => todo!("unexpected toplevel token"),
};
}
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> {
self.devour_whitespace();
let iter1 = self
@ -230,6 +272,9 @@ impl<'a> StyleSheetParser<'a> {
rules,
}));
self.scope -= 1;
if self.scope == 0 {
return stmts;
}
}
Expr::VariableDecl(name, val) => {
if self.scope == 0 {