2020-01-12 20:55:24 -05:00
|
|
|
use std::collections::HashMap;
|
2020-01-04 22:55:04 -05:00
|
|
|
use std::convert::TryFrom;
|
|
|
|
use std::fmt::{self, Display};
|
|
|
|
|
2020-01-25 12:47:38 -05:00
|
|
|
use crate::function::Function;
|
2020-01-12 19:56:33 -05:00
|
|
|
use crate::mixin::Mixin;
|
2020-01-26 09:13:39 -05:00
|
|
|
use crate::value::Value;
|
2020-01-12 19:56:33 -05:00
|
|
|
|
2020-01-04 22:55:04 -05:00
|
|
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
|
|
|
pub enum Symbol {
|
|
|
|
/// .
|
|
|
|
Period,
|
|
|
|
/// #
|
|
|
|
Hash,
|
|
|
|
/// @
|
|
|
|
At,
|
|
|
|
/// $
|
|
|
|
Dollar,
|
|
|
|
/// (
|
|
|
|
OpenParen,
|
|
|
|
/// )
|
|
|
|
CloseParen,
|
|
|
|
/// {
|
2020-01-12 19:56:33 -05:00
|
|
|
OpenCurlyBrace,
|
2020-01-04 22:55:04 -05:00
|
|
|
/// }
|
2020-01-12 19:56:33 -05:00
|
|
|
CloseCurlyBrace,
|
2020-01-04 22:55:04 -05:00
|
|
|
/// [
|
2020-01-12 19:56:33 -05:00
|
|
|
OpenSquareBrace,
|
2020-01-04 22:55:04 -05:00
|
|
|
/// ]
|
2020-01-12 19:56:33 -05:00
|
|
|
CloseSquareBrace,
|
2020-01-04 22:55:04 -05:00
|
|
|
/// ,
|
|
|
|
Comma,
|
|
|
|
/// +
|
|
|
|
Plus,
|
|
|
|
/// -
|
|
|
|
Minus,
|
|
|
|
/// *
|
|
|
|
Mul,
|
|
|
|
/// /
|
|
|
|
Div,
|
|
|
|
/// :
|
|
|
|
Colon,
|
|
|
|
/// ;
|
|
|
|
SemiColon,
|
|
|
|
/// ~
|
|
|
|
Tilde,
|
|
|
|
/// >
|
|
|
|
Gt,
|
|
|
|
/// <
|
|
|
|
Lt,
|
|
|
|
/// ^
|
|
|
|
Xor,
|
|
|
|
/// =
|
|
|
|
Equal,
|
|
|
|
/// |
|
|
|
|
BitOr,
|
|
|
|
/// &
|
|
|
|
BitAnd,
|
|
|
|
/// %
|
|
|
|
Percent,
|
|
|
|
/// "
|
|
|
|
DoubleQuote,
|
|
|
|
/// '
|
|
|
|
SingleQuote,
|
2020-01-20 11:39:05 -05:00
|
|
|
/// ?
|
|
|
|
QuestionMark,
|
|
|
|
/// \
|
|
|
|
BackSlash,
|
2020-01-04 22:55:04 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Display for Symbol {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
match self {
|
2020-01-18 19:11:19 -05:00
|
|
|
Self::Period => write!(f, "."),
|
|
|
|
Self::Hash => write!(f, "#"),
|
|
|
|
Self::At => write!(f, "@"),
|
|
|
|
Self::Dollar => write!(f, "$"),
|
|
|
|
Self::OpenParen => write!(f, "("),
|
|
|
|
Self::CloseParen => write!(f, "),"),
|
|
|
|
Self::OpenCurlyBrace => write!(f, "{{"),
|
|
|
|
Self::CloseCurlyBrace => write!(f, "}}"),
|
|
|
|
Self::OpenSquareBrace => write!(f, "["),
|
|
|
|
Self::CloseSquareBrace => write!(f, "]"),
|
|
|
|
Self::Comma => write!(f, ","),
|
|
|
|
Self::Plus => write!(f, "+"),
|
|
|
|
Self::Minus => write!(f, "-"),
|
|
|
|
Self::Mul => write!(f, "*"),
|
|
|
|
Self::Div => write!(f, "/"),
|
|
|
|
Self::Colon => write!(f, ":"),
|
|
|
|
Self::SemiColon => write!(f, ";"),
|
|
|
|
Self::Tilde => write!(f, "~"),
|
|
|
|
Self::Gt => write!(f, ">"),
|
|
|
|
Self::Lt => write!(f, "<"),
|
|
|
|
Self::Xor => write!(f, "^"),
|
|
|
|
Self::Equal => write!(f, "="),
|
|
|
|
Self::BitOr => write!(f, "|"),
|
|
|
|
Self::BitAnd => write!(f, "&"),
|
|
|
|
Self::Percent => write!(f, "%"),
|
|
|
|
Self::DoubleQuote => write!(f, "\""),
|
|
|
|
Self::SingleQuote => write!(f, "'"),
|
2020-01-20 11:39:05 -05:00
|
|
|
Self::QuestionMark => write!(f, "?"),
|
|
|
|
Self::BackSlash => write!(f, "\\"),
|
2020-01-04 22:55:04 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TryFrom<char> for Symbol {
|
|
|
|
type Error = &'static str;
|
|
|
|
|
|
|
|
fn try_from(c: char) -> Result<Self, Self::Error> {
|
|
|
|
match c {
|
2020-01-18 19:11:19 -05:00
|
|
|
'.' => Ok(Self::Period),
|
|
|
|
'#' => Ok(Self::Hash),
|
|
|
|
'@' => Ok(Self::At),
|
|
|
|
'$' => Ok(Self::Dollar),
|
|
|
|
'(' => Ok(Self::OpenParen),
|
|
|
|
')' => Ok(Self::CloseParen),
|
|
|
|
'{' => Ok(Self::OpenCurlyBrace),
|
|
|
|
'}' => Ok(Self::CloseCurlyBrace),
|
|
|
|
'[' => Ok(Self::OpenSquareBrace),
|
|
|
|
']' => Ok(Self::CloseSquareBrace),
|
|
|
|
',' => Ok(Self::Comma),
|
|
|
|
'+' => Ok(Self::Plus),
|
|
|
|
'-' => Ok(Self::Minus),
|
|
|
|
'*' => Ok(Self::Mul),
|
|
|
|
'/' => Ok(Self::Div),
|
|
|
|
':' => Ok(Self::Colon),
|
|
|
|
';' => Ok(Self::SemiColon),
|
|
|
|
'~' => Ok(Self::Tilde),
|
|
|
|
'>' => Ok(Self::Gt),
|
|
|
|
'<' => Ok(Self::Lt),
|
|
|
|
'^' => Ok(Self::Xor),
|
|
|
|
'=' => Ok(Self::Equal),
|
|
|
|
'|' => Ok(Self::BitOr),
|
|
|
|
'&' => Ok(Self::BitAnd),
|
|
|
|
'%' => Ok(Self::Percent),
|
|
|
|
'"' => Ok(Self::DoubleQuote),
|
|
|
|
'\'' => Ok(Self::SingleQuote),
|
2020-01-20 11:39:05 -05:00
|
|
|
'?' => Ok(Self::QuestionMark),
|
|
|
|
'\\' => Ok(Self::BackSlash),
|
2020-01-04 22:55:04 -05:00
|
|
|
_ => Err("invalid symbol"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
|
|
pub struct MediaQuery {}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
|
|
|
pub enum Whitespace {
|
|
|
|
Space,
|
|
|
|
Tab,
|
|
|
|
Newline,
|
|
|
|
CarriageReturn,
|
|
|
|
}
|
|
|
|
|
2020-01-05 19:09:27 -05:00
|
|
|
impl Display for Whitespace {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
match self {
|
2020-01-18 19:11:19 -05:00
|
|
|
Self::Space => write!(f, " "),
|
|
|
|
Self::Tab => write!(f, "\t"),
|
|
|
|
Self::Newline => writeln!(f),
|
|
|
|
Self::CarriageReturn => write!(f, "\r"),
|
2020-01-05 19:09:27 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-04 22:55:04 -05:00
|
|
|
impl TryFrom<char> for Whitespace {
|
|
|
|
type Error = &'static str;
|
|
|
|
|
|
|
|
fn try_from(c: char) -> Result<Self, Self::Error> {
|
|
|
|
match c {
|
2020-01-18 19:11:19 -05:00
|
|
|
' ' => Ok(Self::Space),
|
|
|
|
'\t' => Ok(Self::Tab),
|
|
|
|
'\n' => Ok(Self::Newline),
|
|
|
|
'\r' => Ok(Self::CarriageReturn),
|
2020-01-04 22:55:04 -05:00
|
|
|
_ => Err("invalid whitespace"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-06 00:25:40 -05:00
|
|
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
|
|
|
pub enum Op {
|
|
|
|
Equal,
|
|
|
|
NotEqual,
|
2020-01-12 12:18:44 -05:00
|
|
|
GreaterThan,
|
2020-01-06 00:25:40 -05:00
|
|
|
GreaterThanEqual,
|
2020-01-12 12:18:44 -05:00
|
|
|
LessThan,
|
2020-01-06 00:25:40 -05:00
|
|
|
LessThanEqual,
|
2020-01-25 09:56:27 -05:00
|
|
|
Plus,
|
|
|
|
Minus,
|
|
|
|
Mul,
|
|
|
|
Div,
|
|
|
|
Rem,
|
2020-01-06 00:25:40 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Display for Op {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
match self {
|
2020-01-18 19:11:19 -05:00
|
|
|
Self::Equal => write!(f, "=="),
|
|
|
|
Self::NotEqual => write!(f, "!="),
|
|
|
|
Self::GreaterThanEqual => write!(f, ">="),
|
|
|
|
Self::LessThanEqual => write!(f, "<="),
|
|
|
|
Self::GreaterThan => write!(f, ">"),
|
|
|
|
Self::LessThan => write!(f, "<"),
|
2020-01-25 09:56:27 -05:00
|
|
|
Self::Plus => write!(f, "+"),
|
|
|
|
Self::Minus => write!(f, "-"),
|
|
|
|
Self::Mul => write!(f, "*"),
|
|
|
|
Self::Div => write!(f, "/"),
|
|
|
|
Self::Rem => write!(f, "%"),
|
2020-01-06 00:25:40 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-04 22:55:04 -05:00
|
|
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
|
|
|
pub enum Keyword {
|
|
|
|
Important,
|
2020-01-06 00:25:40 -05:00
|
|
|
True,
|
|
|
|
False,
|
|
|
|
Null,
|
2020-01-29 21:02:32 -05:00
|
|
|
Default,
|
2020-01-26 12:34:04 -05:00
|
|
|
// Infinity,
|
|
|
|
// NaN,
|
|
|
|
// Auto,
|
|
|
|
// Inherit,
|
|
|
|
// Initial,
|
|
|
|
// Unset,
|
|
|
|
// Not,
|
|
|
|
// And,
|
|
|
|
// Or,
|
|
|
|
// In,
|
2020-01-04 22:55:04 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Display for Keyword {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
match self {
|
2020-01-18 19:11:19 -05:00
|
|
|
Self::Important => write!(f, "!important"),
|
|
|
|
Self::True => write!(f, "true"),
|
|
|
|
Self::False => write!(f, "false"),
|
|
|
|
Self::Null => write!(f, "null"),
|
2020-01-29 21:02:32 -05:00
|
|
|
Self::Default => write!(f, "!default"),
|
2020-01-26 12:34:04 -05:00
|
|
|
// Self::Infinity => write!(f, "Infinity"),
|
|
|
|
// Self::NaN => write!(f, "NaN"),
|
|
|
|
// Self::Auto => write!(f, "auto"),
|
|
|
|
// Self::Inherit => write!(f, "inherit"),
|
|
|
|
// Self::Initial => write!(f, "initial"),
|
|
|
|
// Self::Unset => write!(f, "unset"),
|
|
|
|
// Self::Not => write!(f, "not"),
|
|
|
|
// Self::And => write!(f, "and"),
|
|
|
|
// Self::Or => write!(f, "or"),
|
|
|
|
// Self::In => write!(f, "in"),
|
2020-01-04 22:55:04 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Into<&'static str> for Keyword {
|
|
|
|
fn into(self) -> &'static str {
|
|
|
|
match self {
|
2020-01-18 19:11:19 -05:00
|
|
|
Self::Important => "!important",
|
|
|
|
Self::True => "true",
|
|
|
|
Self::False => "false",
|
|
|
|
Self::Null => "null",
|
2020-01-29 21:02:32 -05:00
|
|
|
Self::Default => "!default",
|
2020-01-26 12:34:04 -05:00
|
|
|
// Self::Infinity => "Infinity",
|
|
|
|
// Self::NaN => "NaN",
|
|
|
|
// Self::Auto => "auto",
|
|
|
|
// Self::Inherit => "inherit",
|
|
|
|
// Self::Initial => "initial",
|
|
|
|
// Self::Unset => "unset",
|
|
|
|
// Self::Not => "not",
|
|
|
|
// Self::And => "and",
|
|
|
|
// Self::Or => "or",
|
|
|
|
// Self::In => "in",
|
2020-01-04 22:55:04 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TryFrom<&str> for Keyword {
|
|
|
|
type Error = &'static str;
|
|
|
|
|
|
|
|
fn try_from(kw: &str) -> Result<Self, Self::Error> {
|
|
|
|
// todo: case insensitive?
|
|
|
|
match kw {
|
2020-01-18 19:11:19 -05:00
|
|
|
"important" => Ok(Self::Important),
|
|
|
|
"true" => Ok(Self::True),
|
|
|
|
"false" => Ok(Self::False),
|
|
|
|
"null" => Ok(Self::Null),
|
2020-01-29 21:02:32 -05:00
|
|
|
"default" => Ok(Self::Default),
|
2020-01-26 12:34:04 -05:00
|
|
|
// "infinity" => Ok(Self::Infinity),
|
|
|
|
// "nan" => Ok(Self::NaN),
|
|
|
|
// "auto" => Ok(Self::Auto),
|
|
|
|
// "inherit" => Ok(Self::Inherit),
|
|
|
|
// "initial" => Ok(Self::Initial),
|
|
|
|
// "unset" => Ok(Self::Unset),
|
|
|
|
// "not" => Ok(Self::Not),
|
|
|
|
// "and" => Ok(Self::And),
|
|
|
|
// "or" => Ok(Self::Or),
|
|
|
|
// "in" => Ok(Self::In),
|
2020-01-04 22:55:04 -05:00
|
|
|
_ => Err("invalid keyword"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
|
|
|
pub struct Pos {
|
|
|
|
line: u32,
|
|
|
|
column: u32,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Pos {
|
|
|
|
pub const fn new() -> Self {
|
2020-01-07 18:37:28 -05:00
|
|
|
Pos { line: 1, column: 1 }
|
2020-01-04 22:55:04 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
pub const fn line(self) -> u32 {
|
|
|
|
self.line
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const fn column(self) -> u32 {
|
|
|
|
self.column
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn newline(&mut self) {
|
|
|
|
self.line += 1;
|
|
|
|
self.column = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn next_char(&mut self) {
|
|
|
|
self.column += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn chars(&mut self, num: u32) {
|
|
|
|
self.column += num;
|
|
|
|
}
|
|
|
|
}
|
2020-01-06 18:26:32 -05:00
|
|
|
|
|
|
|
impl Display for Pos {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
write!(f, "line:{} col:{}", self.line, self.column)
|
|
|
|
}
|
2020-01-06 19:23:52 -05:00
|
|
|
}
|
2020-01-12 19:56:33 -05:00
|
|
|
|
2020-01-26 09:13:39 -05:00
|
|
|
#[derive(Debug, Clone)]
|
2020-01-20 13:15:47 -05:00
|
|
|
pub(crate) struct Scope {
|
2020-02-08 17:26:01 -05:00
|
|
|
vars: HashMap<String, Value>,
|
|
|
|
mixins: HashMap<String, Mixin>,
|
|
|
|
functions: HashMap<String, Function>,
|
2020-01-12 19:56:33 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Scope {
|
|
|
|
#[must_use]
|
|
|
|
pub fn new() -> Self {
|
|
|
|
Self {
|
|
|
|
vars: HashMap::new(),
|
|
|
|
mixins: HashMap::new(),
|
2020-01-25 12:47:38 -05:00
|
|
|
functions: HashMap::new(),
|
2020-01-12 19:56:33 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-08 17:26:01 -05:00
|
|
|
pub fn get_var(&self, v: &str) -> Result<&Value, String> {
|
|
|
|
match self.vars.get(&v.replace('_', "-")) {
|
|
|
|
Some(v) => Ok(v),
|
|
|
|
None => Err(format!("Undefined variable `{}`.", v))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn insert_var(&mut self, s: &str, v: Value) -> Option<Value> {
|
|
|
|
self.vars.insert(s.replace('_', "-"), v)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn var_exists(&self, v: &str) -> bool {
|
|
|
|
self.vars.contains_key(&v.replace('_', "-"))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_mixin(&self, v: &str) -> Result<&Mixin, String> {
|
|
|
|
match self.mixins.get(&v.replace('_', "-")) {
|
|
|
|
Some(v) => Ok(v),
|
|
|
|
None => Err(format!("Undefined mixin `{}`.", v))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn insert_mixin(&mut self, s: &str, v: Mixin) -> Option<Mixin> {
|
|
|
|
self.mixins.insert(s.replace('_', "-"), v)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn mixin_exists(&self, v: &str) -> bool {
|
|
|
|
self.mixins.contains_key(&v.replace('_', "-"))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_fn(&self, v: &str) -> Result<&Function, String> {
|
|
|
|
match self.functions.get(&v.replace('_', "-")) {
|
|
|
|
Some(v) => Ok(v),
|
|
|
|
None => Err(format!("Undefined function `{}`.", v))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn insert_fn(&mut self, s: &str, v: Function) -> Option<Function> {
|
|
|
|
self.functions.insert(s.replace('_', "-"), v)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn fn_exists(&self, v: &str) -> bool {
|
|
|
|
self.functions.contains_key(&v.replace('_', "-"))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn extend(&mut self, other: Scope) {
|
2020-01-12 19:56:33 -05:00
|
|
|
self.vars.extend(other.vars);
|
|
|
|
self.mixins.extend(other.mixins);
|
2020-01-25 12:47:38 -05:00
|
|
|
self.functions.extend(other.functions);
|
2020-01-12 19:56:33 -05:00
|
|
|
}
|
2020-01-12 20:55:24 -05:00
|
|
|
}
|
2020-01-26 16:23:37 -05:00
|
|
|
|
|
|
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
|
|
|
pub(crate) enum QuoteKind {
|
|
|
|
Single,
|
|
|
|
Double,
|
|
|
|
None,
|
|
|
|
}
|
|
|
|
|
2020-02-02 14:46:58 -05:00
|
|
|
impl Display for QuoteKind {
|
|
|
|
#[inline]
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
match self {
|
|
|
|
Self::Single => write!(f, "'"),
|
|
|
|
Self::Double => write!(f, "\""),
|
|
|
|
Self::None => write!(f, ""),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-26 16:23:37 -05:00
|
|
|
impl QuoteKind {
|
|
|
|
pub fn as_str(self) -> &'static str {
|
|
|
|
match self {
|
|
|
|
Self::Single => "'",
|
|
|
|
Self::Double => "\"",
|
|
|
|
Self::None => "",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-02-08 16:07:37 -05:00
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
|
|
pub(crate) enum ListSeparator {
|
|
|
|
Space,
|
|
|
|
Comma,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ListSeparator {
|
|
|
|
pub fn as_str(self) -> &'static str {
|
|
|
|
match self {
|
|
|
|
Self::Space => " ",
|
|
|
|
Self::Comma => ", ",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(dead_code)]
|
|
|
|
pub fn name(self) -> &'static str {
|
|
|
|
match self {
|
|
|
|
Self::Space => "space",
|
|
|
|
Self::Comma => "comma",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Display for ListSeparator {
|
|
|
|
#[inline]
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
match self {
|
|
|
|
Self::Space => write!(f, " "),
|
|
|
|
Self::Comma => write!(f, ", "),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|