implement @use ... as *; syntax

This commit is contained in:
Connor Skees 2020-07-27 18:55:38 -04:00
parent 36d7b5d920
commit d6a1d64dcb
10 changed files with 98 additions and 94 deletions

View File

@ -181,7 +181,7 @@ pub(crate) fn get_function(mut args: CallArgs, parser: &mut Parser<'_>) -> SassR
},
parser.global_scope,
) {
Some(f) => SassFunction::UserDefined(Box::new(f), name),
Some(f) => f,
None => match GLOBAL_FUNCTIONS.get(name.as_str()) {
Some(f) => SassFunction::Builtin(f.clone(), name),
None => return Err((format!("Function not found: {}", name), args.span()).into()),

View File

@ -24,9 +24,9 @@ mod string;
#[derive(Debug, Default)]
pub(crate) struct Module {
vars: BTreeMap<Identifier, Value>,
mixins: BTreeMap<Identifier, Mixin>,
functions: BTreeMap<Identifier, SassFunction>,
pub vars: BTreeMap<Identifier, Value>,
pub mixins: BTreeMap<Identifier, Mixin>,
pub functions: BTreeMap<Identifier, SassFunction>,
}
impl Module {

View File

@ -1,6 +1,6 @@
use std::{collections::HashMap, mem};
use codemap::{Span, Spanned};
use codemap::Span;
use crate::{
args::{CallArg, CallArgs, FuncArg, FuncArgs},
@ -283,15 +283,8 @@ impl<'a> Parser<'a> {
self.scopes.enter_new_scope();
for (idx, mut arg) in fn_args.0.into_iter().enumerate() {
if arg.is_variadic {
let span = args.span();
let arg_list = Value::ArgList(args.get_variadic()?);
scope.insert_var(
arg.name,
Spanned {
node: arg_list,
span,
},
);
scope.insert_var(arg.name, arg_list);
break;
}
let val = match args.get(idx, arg.name) {
@ -304,7 +297,8 @@ impl<'a> Parser<'a> {
)
}
},
}?;
}?
.node;
self.scopes.insert_var(arg.name, val.clone());
scope.insert_var(arg.name, val);
}

View File

@ -304,10 +304,7 @@ impl<'a> Parser<'a> {
for i in iter {
self.scopes.insert_var_last(
var.node,
Spanned {
node: Value::Dimension(Some(Number::from(i)), Unit::None, true),
span: var.span,
},
Value::Dimension(Some(Number::from(i)), Unit::None, true),
);
if self.flags.in_function() {
let these_stmts = Parser {
@ -487,26 +484,14 @@ impl<'a> Parser<'a> {
for row in iter {
if vars.len() == 1 {
self.scopes.insert_var_last(
vars[0].node,
Spanned {
node: row,
span: vars[0].span,
},
);
self.scopes.insert_var_last(vars[0].node, row);
} else {
for (var, val) in vars.iter().zip(
row.as_list()
.into_iter()
.chain(std::iter::once(Value::Null).cycle()),
) {
self.scopes.insert_var_last(
var.node,
Spanned {
node: val,
span: var.span,
},
);
self.scopes.insert_var_last(var.node, val);
}
}

View File

@ -4,11 +4,11 @@ use peekmore::PeekMore;
use crate::{
args::CallArgs,
atrule::Function,
common::unvendor,
common::{unvendor, Identifier},
error::SassResult,
scope::Scopes,
utils::{read_until_closing_curly_brace, read_until_semicolon_or_closing_curly_brace},
value::Value,
value::{SassFunction, Value},
Token,
};
@ -53,10 +53,18 @@ impl<'a> Parser<'a> {
let function = Function::new(args, body, self.at_root, span);
let name_as_ident = Identifier::from(name);
if self.at_root {
self.global_scope.insert_fn(name, function);
self.global_scope.insert_fn(
name_as_ident,
SassFunction::UserDefined(Box::new(function), name_as_ident),
);
} else {
self.scopes.insert_fn(name.into(), function);
self.scopes.insert_fn(
name_as_ident,
SassFunction::UserDefined(Box::new(function), name_as_ident),
);
}
Ok(())
}

View File

@ -150,11 +150,11 @@ impl<'a> Parser<'a> {
};
let Spanned { node: module, span } = self.parse_quoted_string(quote)?;
let module = module.unquote().to_css_string(span)?;
let module_name = module.unquote().to_css_string(span)?;
self.whitespace_or_comment();
let mut module_name: Option<String> = None;
let mut module_alias: Option<String> = None;
match self.toks.peek() {
Some(Token { kind: ';', .. }) => {
@ -170,12 +170,21 @@ impl<'a> Parser<'a> {
self.whitespace_or_comment();
let name = self.parse_identifier_no_interpolation(false)?;
let name_span;
module_name = Some(name.node);
if let Some(Token { kind: '*', pos }) = self.toks.peek() {
name_span = *pos;
self.toks.next();
module_alias = Some('*'.to_string());
} else {
let name = self.parse_identifier_no_interpolation(false)?;
module_alias = Some(name.node);
name_span = name.span;
}
if !matches!(self.toks.next(), Some(Token { kind: ';', .. })) {
return Err(("expected \";\".", name.span).into());
return Err(("expected \";\".", name_span).into());
}
}
Some(Token { kind: 'w', .. }) | Some(Token { kind: 'W', .. }) => {
@ -184,37 +193,36 @@ impl<'a> Parser<'a> {
Some(..) | None => return Err(("expected \";\".", span).into()),
}
match module.as_ref() {
"sass:color" => self.modules.insert(
module_name.unwrap_or_else(|| "color".to_owned()),
declare_module_color(),
),
"sass:list" => self.modules.insert(
module_name.unwrap_or_else(|| "list".to_owned()),
declare_module_list(),
),
"sass:map" => self.modules.insert(
module_name.unwrap_or_else(|| "map".to_owned()),
declare_module_map(),
),
"sass:math" => self.modules.insert(
module_name.unwrap_or_else(|| "math".to_owned()),
declare_module_math(),
),
"sass:meta" => self.modules.insert(
module_name.unwrap_or_else(|| "meta".to_owned()),
declare_module_meta(),
),
"sass:selector" => self.modules.insert(
module_name.unwrap_or_else(|| "selector".to_owned()),
declare_module_selector(),
),
"sass:string" => self.modules.insert(
module_name.unwrap_or_else(|| "string".to_owned()),
declare_module_string(),
),
let module = match module_name.as_ref() {
"sass:color" => declare_module_color(),
"sass:list" => declare_module_list(),
"sass:map" => declare_module_map(),
"sass:math" => declare_module_math(),
"sass:meta" => declare_module_meta(),
"sass:selector" => declare_module_selector(),
"sass:string" => declare_module_string(),
_ => todo!("@use not yet implemented"),
};
let module_name = match module_alias.as_deref() {
Some("*") => {
self.global_scope.merge_module(module);
continue;
}
Some(..) => module_alias.unwrap(),
None => match module_name.as_ref() {
"sass:color" => "color".to_owned(),
"sass:list" => "list".to_owned(),
"sass:map" => "map".to_owned(),
"sass:math" => "math".to_owned(),
"sass:meta" => "meta".to_owned(),
"sass:selector" => "selector".to_owned(),
"sass:string" => "string".to_owned(),
_ => module_name.into_owned(),
},
};
self.modules.insert(module_name, module);
}
Some(Token { kind: '/', .. }) => {
self.toks.next();

View File

@ -355,8 +355,7 @@ impl<'a> Parser<'a> {
let call_args = self.parse_call_args()?;
return Ok(IntermediateValue::Value(HigherIntermediateValue::Function(
SassFunction::UserDefined(Box::new(func), as_ident),
call_args,
func, call_args,
))
.span(span));
}

View File

@ -41,11 +41,11 @@ impl<'a> Parser<'a> {
if default {
if self.at_root && !self.flags.in_control_flow() {
if !self.global_scope.var_exists(ident) {
let value = self.parse_value_from_vec(val_toks, true)?;
let value = self.parse_value_from_vec(val_toks, true)?.node;
self.global_scope.insert_var(ident, value);
}
} else {
let value = self.parse_value_from_vec(val_toks, true)?;
let value = self.parse_value_from_vec(val_toks, true)?.node;
if global && !self.global_scope.var_exists(ident) {
self.global_scope.insert_var(ident, value.clone());
}
@ -55,7 +55,7 @@ impl<'a> Parser<'a> {
return Ok(());
}
let value = self.parse_value_from_vec(val_toks, true)?;
let value = self.parse_value_from_vec(val_toks, true)?.node;
if global {
self.global_scope.insert_var(ident, value.clone());

View File

@ -3,18 +3,18 @@ use std::collections::BTreeMap;
use codemap::Spanned;
use crate::{
atrule::{Function, Mixin},
builtin::GLOBAL_FUNCTIONS,
atrule::Mixin,
builtin::{modules::Module, GLOBAL_FUNCTIONS},
common::Identifier,
error::SassResult,
value::Value,
value::{SassFunction, Value},
};
#[derive(Debug, Default)]
pub(crate) struct Scope {
vars: BTreeMap<Identifier, Spanned<Value>>,
vars: BTreeMap<Identifier, Value>,
mixins: BTreeMap<Identifier, Mixin>,
functions: BTreeMap<Identifier, Function>,
functions: BTreeMap<Identifier, SassFunction>,
}
impl Scope {
@ -31,12 +31,12 @@ impl Scope {
fn get_var(&self, name: Spanned<Identifier>) -> SassResult<&Value> {
match self.vars.get(&name.node) {
Some(v) => Ok(&v.node),
Some(v) => Ok(v),
None => Err(("Undefined variable.", name.span).into()),
}
}
pub fn insert_var(&mut self, s: Identifier, v: Spanned<Value>) -> Option<Spanned<Value>> {
pub fn insert_var(&mut self, s: Identifier, v: Value) -> Option<Value> {
self.vars.insert(s, v)
}
@ -59,12 +59,12 @@ impl Scope {
self.mixins.contains_key(&name)
}
fn get_fn(&self, name: Identifier) -> Option<Function> {
fn get_fn(&self, name: Identifier) -> Option<SassFunction> {
self.functions.get(&name).cloned()
}
pub fn insert_fn<T: Into<Identifier>>(&mut self, s: T, v: Function) -> Option<Function> {
self.functions.insert(s.into(), v)
pub fn insert_fn(&mut self, s: Identifier, v: SassFunction) -> Option<SassFunction> {
self.functions.insert(s, v)
}
fn fn_exists(&self, name: Identifier) -> bool {
@ -79,6 +79,12 @@ impl Scope {
self.mixins.extend(other.mixins);
self.functions.extend(other.functions);
}
pub fn merge_module(&mut self, other: Module) {
self.vars.extend(other.vars);
self.mixins.extend(other.mixins);
self.functions.extend(other.functions);
}
}
#[derive(Debug, Default)]
@ -112,7 +118,7 @@ impl Scopes {
/// Variables
impl Scopes {
pub fn insert_var(&mut self, s: Identifier, v: Spanned<Value>) -> Option<Spanned<Value>> {
pub fn insert_var(&mut self, s: Identifier, v: Value) -> Option<Value> {
for scope in self.0.iter_mut().rev() {
if scope.var_exists(s) {
return scope.insert_var(s, v);
@ -131,7 +137,7 @@ impl Scopes {
/// Always insert this variable into the innermost scope
///
/// Used, for example, for variables from `@each` and `@for`
pub fn insert_var_last(&mut self, s: Identifier, v: Spanned<Value>) -> Option<Spanned<Value>> {
pub fn insert_var_last(&mut self, s: Identifier, v: Value) -> Option<Value> {
if let Some(scope) = self.0.last_mut() {
scope.insert_var(s, v)
} else {
@ -142,11 +148,7 @@ impl Scopes {
}
}
pub fn insert_default_var(
&mut self,
s: Identifier,
v: Spanned<Value>,
) -> Option<Spanned<Value>> {
pub fn insert_default_var(&mut self, s: Identifier, v: Value) -> Option<Value> {
if let Some(scope) = self.0.last_mut() {
if scope.var_exists(s) {
None
@ -219,7 +221,7 @@ impl Scopes {
/// Functions
impl Scopes {
pub fn insert_fn(&mut self, s: Identifier, v: Function) -> Option<Function> {
pub fn insert_fn(&mut self, s: Identifier, v: SassFunction) -> Option<SassFunction> {
if let Some(scope) = self.0.last_mut() {
scope.insert_fn(s, v)
} else {
@ -234,7 +236,7 @@ impl Scopes {
&'a self,
name: Spanned<Identifier>,
global_scope: &'a Scope,
) -> Option<Function> {
) -> Option<SassFunction> {
for scope in self.0.iter().rev() {
if scope.fn_exists(name.node) {
return scope.get_fn(name.node);

View File

@ -50,3 +50,11 @@ test!(
}",
"a {\n color: 1;\n}\n"
);
test!(
use_as_universal,
"@use \"sass:math\" as *;
a {
color: cos(2);
}",
"a {\n color: -0.4161468365;\n}\n"
);