implement @use ... as *;
syntax
This commit is contained in:
parent
36d7b5d920
commit
d6a1d64dcb
@ -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()),
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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(())
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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());
|
||||
|
40
src/scope.rs
40
src/scope.rs
@ -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);
|
||||
|
@ -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"
|
||||
);
|
||||
|
Loading…
x
Reference in New Issue
Block a user