Implement basic at rule parsing and @error
This commit is contained in:
parent
46a6fd90f6
commit
8320c3de77
@ -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 {
|
||||
|
18
src/lexer.rs
18
src/lexer.rs
@ -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 `@`")
|
||||
}
|
||||
|
69
src/main.rs
69
src/main.rs
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user