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,
|
parser.global_scope,
|
||||||
) {
|
) {
|
||||||
Some(f) => SassFunction::UserDefined(Box::new(f), name),
|
Some(f) => f,
|
||||||
None => match GLOBAL_FUNCTIONS.get(name.as_str()) {
|
None => match GLOBAL_FUNCTIONS.get(name.as_str()) {
|
||||||
Some(f) => SassFunction::Builtin(f.clone(), name),
|
Some(f) => SassFunction::Builtin(f.clone(), name),
|
||||||
None => return Err((format!("Function not found: {}", name), args.span()).into()),
|
None => return Err((format!("Function not found: {}", name), args.span()).into()),
|
||||||
|
@ -24,9 +24,9 @@ mod string;
|
|||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub(crate) struct Module {
|
pub(crate) struct Module {
|
||||||
vars: BTreeMap<Identifier, Value>,
|
pub vars: BTreeMap<Identifier, Value>,
|
||||||
mixins: BTreeMap<Identifier, Mixin>,
|
pub mixins: BTreeMap<Identifier, Mixin>,
|
||||||
functions: BTreeMap<Identifier, SassFunction>,
|
pub functions: BTreeMap<Identifier, SassFunction>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Module {
|
impl Module {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::{collections::HashMap, mem};
|
use std::{collections::HashMap, mem};
|
||||||
|
|
||||||
use codemap::{Span, Spanned};
|
use codemap::Span;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
args::{CallArg, CallArgs, FuncArg, FuncArgs},
|
args::{CallArg, CallArgs, FuncArg, FuncArgs},
|
||||||
@ -283,15 +283,8 @@ impl<'a> Parser<'a> {
|
|||||||
self.scopes.enter_new_scope();
|
self.scopes.enter_new_scope();
|
||||||
for (idx, mut arg) in fn_args.0.into_iter().enumerate() {
|
for (idx, mut arg) in fn_args.0.into_iter().enumerate() {
|
||||||
if arg.is_variadic {
|
if arg.is_variadic {
|
||||||
let span = args.span();
|
|
||||||
let arg_list = Value::ArgList(args.get_variadic()?);
|
let arg_list = Value::ArgList(args.get_variadic()?);
|
||||||
scope.insert_var(
|
scope.insert_var(arg.name, arg_list);
|
||||||
arg.name,
|
|
||||||
Spanned {
|
|
||||||
node: arg_list,
|
|
||||||
span,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let val = match args.get(idx, arg.name) {
|
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());
|
self.scopes.insert_var(arg.name, val.clone());
|
||||||
scope.insert_var(arg.name, val);
|
scope.insert_var(arg.name, val);
|
||||||
}
|
}
|
||||||
|
@ -304,10 +304,7 @@ impl<'a> Parser<'a> {
|
|||||||
for i in iter {
|
for i in iter {
|
||||||
self.scopes.insert_var_last(
|
self.scopes.insert_var_last(
|
||||||
var.node,
|
var.node,
|
||||||
Spanned {
|
Value::Dimension(Some(Number::from(i)), Unit::None, true),
|
||||||
node: Value::Dimension(Some(Number::from(i)), Unit::None, true),
|
|
||||||
span: var.span,
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
if self.flags.in_function() {
|
if self.flags.in_function() {
|
||||||
let these_stmts = Parser {
|
let these_stmts = Parser {
|
||||||
@ -487,26 +484,14 @@ impl<'a> Parser<'a> {
|
|||||||
|
|
||||||
for row in iter {
|
for row in iter {
|
||||||
if vars.len() == 1 {
|
if vars.len() == 1 {
|
||||||
self.scopes.insert_var_last(
|
self.scopes.insert_var_last(vars[0].node, row);
|
||||||
vars[0].node,
|
|
||||||
Spanned {
|
|
||||||
node: row,
|
|
||||||
span: vars[0].span,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
for (var, val) in vars.iter().zip(
|
for (var, val) in vars.iter().zip(
|
||||||
row.as_list()
|
row.as_list()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.chain(std::iter::once(Value::Null).cycle()),
|
.chain(std::iter::once(Value::Null).cycle()),
|
||||||
) {
|
) {
|
||||||
self.scopes.insert_var_last(
|
self.scopes.insert_var_last(var.node, val);
|
||||||
var.node,
|
|
||||||
Spanned {
|
|
||||||
node: val,
|
|
||||||
span: var.span,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,11 +4,11 @@ use peekmore::PeekMore;
|
|||||||
use crate::{
|
use crate::{
|
||||||
args::CallArgs,
|
args::CallArgs,
|
||||||
atrule::Function,
|
atrule::Function,
|
||||||
common::unvendor,
|
common::{unvendor, Identifier},
|
||||||
error::SassResult,
|
error::SassResult,
|
||||||
scope::Scopes,
|
scope::Scopes,
|
||||||
utils::{read_until_closing_curly_brace, read_until_semicolon_or_closing_curly_brace},
|
utils::{read_until_closing_curly_brace, read_until_semicolon_or_closing_curly_brace},
|
||||||
value::Value,
|
value::{SassFunction, Value},
|
||||||
Token,
|
Token,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -53,10 +53,18 @@ impl<'a> Parser<'a> {
|
|||||||
|
|
||||||
let function = Function::new(args, body, self.at_root, span);
|
let function = Function::new(args, body, self.at_root, span);
|
||||||
|
|
||||||
|
let name_as_ident = Identifier::from(name);
|
||||||
|
|
||||||
if self.at_root {
|
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 {
|
} else {
|
||||||
self.scopes.insert_fn(name.into(), function);
|
self.scopes.insert_fn(
|
||||||
|
name_as_ident,
|
||||||
|
SassFunction::UserDefined(Box::new(function), name_as_ident),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -150,11 +150,11 @@ impl<'a> Parser<'a> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let Spanned { node: module, span } = self.parse_quoted_string(quote)?;
|
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();
|
self.whitespace_or_comment();
|
||||||
|
|
||||||
let mut module_name: Option<String> = None;
|
let mut module_alias: Option<String> = None;
|
||||||
|
|
||||||
match self.toks.peek() {
|
match self.toks.peek() {
|
||||||
Some(Token { kind: ';', .. }) => {
|
Some(Token { kind: ';', .. }) => {
|
||||||
@ -170,12 +170,21 @@ impl<'a> Parser<'a> {
|
|||||||
|
|
||||||
self.whitespace_or_comment();
|
self.whitespace_or_comment();
|
||||||
|
|
||||||
|
let name_span;
|
||||||
|
|
||||||
|
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)?;
|
let name = self.parse_identifier_no_interpolation(false)?;
|
||||||
|
|
||||||
module_name = Some(name.node);
|
module_alias = Some(name.node);
|
||||||
|
name_span = name.span;
|
||||||
|
}
|
||||||
|
|
||||||
if !matches!(self.toks.next(), Some(Token { kind: ';', .. })) {
|
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', .. }) => {
|
Some(Token { kind: 'w', .. }) | Some(Token { kind: 'W', .. }) => {
|
||||||
@ -184,37 +193,36 @@ impl<'a> Parser<'a> {
|
|||||||
Some(..) | None => return Err(("expected \";\".", span).into()),
|
Some(..) | None => return Err(("expected \";\".", span).into()),
|
||||||
}
|
}
|
||||||
|
|
||||||
match module.as_ref() {
|
let module = match module_name.as_ref() {
|
||||||
"sass:color" => self.modules.insert(
|
"sass:color" => declare_module_color(),
|
||||||
module_name.unwrap_or_else(|| "color".to_owned()),
|
"sass:list" => declare_module_list(),
|
||||||
declare_module_color(),
|
"sass:map" => declare_module_map(),
|
||||||
),
|
"sass:math" => declare_module_math(),
|
||||||
"sass:list" => self.modules.insert(
|
"sass:meta" => declare_module_meta(),
|
||||||
module_name.unwrap_or_else(|| "list".to_owned()),
|
"sass:selector" => declare_module_selector(),
|
||||||
declare_module_list(),
|
"sass:string" => declare_module_string(),
|
||||||
),
|
|
||||||
"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(),
|
|
||||||
),
|
|
||||||
_ => todo!("@use not yet implemented"),
|
_ => 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: '/', .. }) => {
|
Some(Token { kind: '/', .. }) => {
|
||||||
self.toks.next();
|
self.toks.next();
|
||||||
|
@ -355,8 +355,7 @@ impl<'a> Parser<'a> {
|
|||||||
|
|
||||||
let call_args = self.parse_call_args()?;
|
let call_args = self.parse_call_args()?;
|
||||||
return Ok(IntermediateValue::Value(HigherIntermediateValue::Function(
|
return Ok(IntermediateValue::Value(HigherIntermediateValue::Function(
|
||||||
SassFunction::UserDefined(Box::new(func), as_ident),
|
func, call_args,
|
||||||
call_args,
|
|
||||||
))
|
))
|
||||||
.span(span));
|
.span(span));
|
||||||
}
|
}
|
||||||
|
@ -41,11 +41,11 @@ impl<'a> Parser<'a> {
|
|||||||
if default {
|
if default {
|
||||||
if self.at_root && !self.flags.in_control_flow() {
|
if self.at_root && !self.flags.in_control_flow() {
|
||||||
if !self.global_scope.var_exists(ident) {
|
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);
|
self.global_scope.insert_var(ident, value);
|
||||||
}
|
}
|
||||||
} else {
|
} 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) {
|
if global && !self.global_scope.var_exists(ident) {
|
||||||
self.global_scope.insert_var(ident, value.clone());
|
self.global_scope.insert_var(ident, value.clone());
|
||||||
}
|
}
|
||||||
@ -55,7 +55,7 @@ impl<'a> Parser<'a> {
|
|||||||
return Ok(());
|
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 {
|
if global {
|
||||||
self.global_scope.insert_var(ident, value.clone());
|
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 codemap::Spanned;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
atrule::{Function, Mixin},
|
atrule::Mixin,
|
||||||
builtin::GLOBAL_FUNCTIONS,
|
builtin::{modules::Module, GLOBAL_FUNCTIONS},
|
||||||
common::Identifier,
|
common::Identifier,
|
||||||
error::SassResult,
|
error::SassResult,
|
||||||
value::Value,
|
value::{SassFunction, Value},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub(crate) struct Scope {
|
pub(crate) struct Scope {
|
||||||
vars: BTreeMap<Identifier, Spanned<Value>>,
|
vars: BTreeMap<Identifier, Value>,
|
||||||
mixins: BTreeMap<Identifier, Mixin>,
|
mixins: BTreeMap<Identifier, Mixin>,
|
||||||
functions: BTreeMap<Identifier, Function>,
|
functions: BTreeMap<Identifier, SassFunction>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Scope {
|
impl Scope {
|
||||||
@ -31,12 +31,12 @@ impl Scope {
|
|||||||
|
|
||||||
fn get_var(&self, name: Spanned<Identifier>) -> SassResult<&Value> {
|
fn get_var(&self, name: Spanned<Identifier>) -> SassResult<&Value> {
|
||||||
match self.vars.get(&name.node) {
|
match self.vars.get(&name.node) {
|
||||||
Some(v) => Ok(&v.node),
|
Some(v) => Ok(v),
|
||||||
None => Err(("Undefined variable.", name.span).into()),
|
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)
|
self.vars.insert(s, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,12 +59,12 @@ impl Scope {
|
|||||||
self.mixins.contains_key(&name)
|
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()
|
self.functions.get(&name).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_fn<T: Into<Identifier>>(&mut self, s: T, v: Function) -> Option<Function> {
|
pub fn insert_fn(&mut self, s: Identifier, v: SassFunction) -> Option<SassFunction> {
|
||||||
self.functions.insert(s.into(), v)
|
self.functions.insert(s, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fn_exists(&self, name: Identifier) -> bool {
|
fn fn_exists(&self, name: Identifier) -> bool {
|
||||||
@ -79,6 +79,12 @@ impl Scope {
|
|||||||
self.mixins.extend(other.mixins);
|
self.mixins.extend(other.mixins);
|
||||||
self.functions.extend(other.functions);
|
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)]
|
#[derive(Debug, Default)]
|
||||||
@ -112,7 +118,7 @@ impl Scopes {
|
|||||||
|
|
||||||
/// Variables
|
/// Variables
|
||||||
impl Scopes {
|
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() {
|
for scope in self.0.iter_mut().rev() {
|
||||||
if scope.var_exists(s) {
|
if scope.var_exists(s) {
|
||||||
return scope.insert_var(s, v);
|
return scope.insert_var(s, v);
|
||||||
@ -131,7 +137,7 @@ impl Scopes {
|
|||||||
/// Always insert this variable into the innermost scope
|
/// Always insert this variable into the innermost scope
|
||||||
///
|
///
|
||||||
/// Used, for example, for variables from `@each` and `@for`
|
/// 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() {
|
if let Some(scope) = self.0.last_mut() {
|
||||||
scope.insert_var(s, v)
|
scope.insert_var(s, v)
|
||||||
} else {
|
} else {
|
||||||
@ -142,11 +148,7 @@ impl Scopes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_default_var(
|
pub fn insert_default_var(&mut self, s: Identifier, v: Value) -> Option<Value> {
|
||||||
&mut self,
|
|
||||||
s: Identifier,
|
|
||||||
v: Spanned<Value>,
|
|
||||||
) -> Option<Spanned<Value>> {
|
|
||||||
if let Some(scope) = self.0.last_mut() {
|
if let Some(scope) = self.0.last_mut() {
|
||||||
if scope.var_exists(s) {
|
if scope.var_exists(s) {
|
||||||
None
|
None
|
||||||
@ -219,7 +221,7 @@ impl Scopes {
|
|||||||
|
|
||||||
/// Functions
|
/// Functions
|
||||||
impl Scopes {
|
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() {
|
if let Some(scope) = self.0.last_mut() {
|
||||||
scope.insert_fn(s, v)
|
scope.insert_fn(s, v)
|
||||||
} else {
|
} else {
|
||||||
@ -234,7 +236,7 @@ impl Scopes {
|
|||||||
&'a self,
|
&'a self,
|
||||||
name: Spanned<Identifier>,
|
name: Spanned<Identifier>,
|
||||||
global_scope: &'a Scope,
|
global_scope: &'a Scope,
|
||||||
) -> Option<Function> {
|
) -> Option<SassFunction> {
|
||||||
for scope in self.0.iter().rev() {
|
for scope in self.0.iter().rev() {
|
||||||
if scope.fn_exists(name.node) {
|
if scope.fn_exists(name.node) {
|
||||||
return scope.get_fn(name.node);
|
return scope.get_fn(name.node);
|
||||||
|
@ -50,3 +50,11 @@ test!(
|
|||||||
}",
|
}",
|
||||||
"a {\n color: 1;\n}\n"
|
"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