2020-07-08 10:15:54 -04:00
|
|
|
use codemap::Spanned;
|
|
|
|
use num_traits::cast::ToPrimitive;
|
|
|
|
use peekmore::PeekMore;
|
|
|
|
|
|
|
|
use crate::{
|
|
|
|
common::Identifier,
|
|
|
|
error::SassResult,
|
|
|
|
parse::{ContextFlags, Parser, Stmt},
|
|
|
|
unit::Unit,
|
|
|
|
utils::{
|
2020-08-07 17:33:30 -04:00
|
|
|
peek_ident_no_interpolation, read_until_closing_curly_brace, read_until_open_curly_brace,
|
2020-07-08 10:15:54 -04:00
|
|
|
},
|
|
|
|
value::{Number, Value},
|
|
|
|
Token,
|
|
|
|
};
|
|
|
|
|
|
|
|
impl<'a> Parser<'a> {
|
|
|
|
pub(super) fn parse_if(&mut self) -> SassResult<Vec<Stmt>> {
|
|
|
|
self.whitespace_or_comment();
|
|
|
|
|
|
|
|
let mut found_true = false;
|
|
|
|
let mut body = Vec::new();
|
|
|
|
|
2020-08-08 14:36:59 -04:00
|
|
|
let init_cond = self.parse_value(true, &|_| false)?.node;
|
2020-07-08 10:15:54 -04:00
|
|
|
|
2020-08-06 21:36:11 -04:00
|
|
|
self.expect_char('{')?;
|
2020-07-08 10:15:54 -04:00
|
|
|
|
|
|
|
if self.toks.peek().is_none() {
|
2020-08-06 21:36:11 -04:00
|
|
|
return Err(("expected \"}\".", self.span_before).into());
|
2020-07-08 10:15:54 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
self.whitespace_or_comment();
|
|
|
|
|
|
|
|
if init_cond.is_true() {
|
2020-08-18 00:17:06 -04:00
|
|
|
self.scopes.enter_new_scope();
|
2020-07-08 10:15:54 -04:00
|
|
|
found_true = true;
|
|
|
|
body = Parser {
|
|
|
|
toks: self.toks,
|
|
|
|
map: self.map,
|
|
|
|
path: self.path,
|
|
|
|
scopes: self.scopes,
|
|
|
|
global_scope: self.global_scope,
|
|
|
|
super_selectors: self.super_selectors,
|
|
|
|
span_before: self.span_before,
|
|
|
|
content: self.content,
|
|
|
|
flags: self.flags | ContextFlags::IN_CONTROL_FLOW,
|
|
|
|
at_root: self.at_root,
|
|
|
|
at_root_has_selector: self.at_root_has_selector,
|
|
|
|
extender: self.extender,
|
2020-07-08 17:52:37 -04:00
|
|
|
content_scopes: self.content_scopes,
|
2020-07-15 12:37:19 +01:00
|
|
|
options: self.options,
|
2020-07-25 19:22:12 -04:00
|
|
|
modules: self.modules,
|
2020-08-06 21:00:34 -04:00
|
|
|
module_config: self.module_config,
|
2020-07-08 10:15:54 -04:00
|
|
|
}
|
|
|
|
.parse_stmt()?;
|
2020-08-18 00:17:06 -04:00
|
|
|
self.scopes.exit_scope();
|
2020-07-08 10:15:54 -04:00
|
|
|
} else {
|
|
|
|
self.throw_away_until_closing_curly_brace()?;
|
|
|
|
}
|
|
|
|
|
|
|
|
loop {
|
|
|
|
self.whitespace_or_comment();
|
|
|
|
if let Some(Token { kind: '@', pos }) = self.toks.peek().cloned() {
|
|
|
|
self.toks.peek_forward(1);
|
|
|
|
let ident = peek_ident_no_interpolation(self.toks, false, pos)?;
|
|
|
|
if ident.as_str() != "else" {
|
|
|
|
self.toks.reset_cursor();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
self.toks.truncate_iterator_to_cursor();
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
self.whitespace_or_comment();
|
|
|
|
if let Some(tok) = self.toks.peek().cloned() {
|
|
|
|
match tok.kind {
|
|
|
|
'i' if matches!(
|
|
|
|
self.toks.peek_forward(1),
|
|
|
|
Some(Token { kind: 'f', .. }) | Some(Token { kind: 'F', .. })
|
|
|
|
) =>
|
|
|
|
{
|
|
|
|
self.toks.next();
|
|
|
|
self.toks.next();
|
|
|
|
let cond = if found_true {
|
|
|
|
self.throw_away_until_open_curly_brace()?;
|
|
|
|
false
|
|
|
|
} else {
|
2020-08-08 14:36:59 -04:00
|
|
|
let v = self.parse_value(true, &|_| false)?.node.is_true();
|
2020-08-06 21:36:11 -04:00
|
|
|
self.expect_char('{')?;
|
2020-07-08 10:15:54 -04:00
|
|
|
v
|
|
|
|
};
|
|
|
|
if cond {
|
|
|
|
found_true = true;
|
2020-08-18 00:17:06 -04:00
|
|
|
self.scopes.enter_new_scope();
|
2020-07-08 10:15:54 -04:00
|
|
|
body = Parser {
|
|
|
|
toks: self.toks,
|
|
|
|
map: self.map,
|
|
|
|
path: self.path,
|
|
|
|
scopes: self.scopes,
|
|
|
|
global_scope: self.global_scope,
|
|
|
|
super_selectors: self.super_selectors,
|
|
|
|
span_before: self.span_before,
|
|
|
|
content: self.content,
|
|
|
|
flags: self.flags | ContextFlags::IN_CONTROL_FLOW,
|
|
|
|
at_root: self.at_root,
|
|
|
|
at_root_has_selector: self.at_root_has_selector,
|
|
|
|
extender: self.extender,
|
2020-07-08 17:52:37 -04:00
|
|
|
content_scopes: self.content_scopes,
|
2020-07-15 12:37:19 +01:00
|
|
|
options: self.options,
|
2020-07-25 19:22:12 -04:00
|
|
|
modules: self.modules,
|
2020-08-06 21:00:34 -04:00
|
|
|
module_config: self.module_config,
|
2020-07-08 10:15:54 -04:00
|
|
|
}
|
|
|
|
.parse_stmt()?;
|
2020-08-18 00:17:06 -04:00
|
|
|
self.scopes.exit_scope();
|
2020-07-08 10:15:54 -04:00
|
|
|
} else {
|
|
|
|
self.throw_away_until_closing_curly_brace()?;
|
|
|
|
}
|
|
|
|
self.whitespace();
|
|
|
|
}
|
|
|
|
'{' => {
|
|
|
|
self.toks.next();
|
|
|
|
if found_true {
|
|
|
|
self.throw_away_until_closing_curly_brace()?;
|
|
|
|
break;
|
|
|
|
} else {
|
2020-08-18 00:17:06 -04:00
|
|
|
self.scopes.enter_new_scope();
|
|
|
|
let tmp = Parser {
|
2020-07-08 10:15:54 -04:00
|
|
|
toks: self.toks,
|
|
|
|
map: self.map,
|
|
|
|
path: self.path,
|
|
|
|
scopes: self.scopes,
|
|
|
|
global_scope: self.global_scope,
|
|
|
|
super_selectors: self.super_selectors,
|
|
|
|
span_before: self.span_before,
|
|
|
|
content: self.content,
|
|
|
|
flags: self.flags | ContextFlags::IN_CONTROL_FLOW,
|
|
|
|
at_root: self.at_root,
|
|
|
|
at_root_has_selector: self.at_root_has_selector,
|
|
|
|
extender: self.extender,
|
2020-07-08 17:52:37 -04:00
|
|
|
content_scopes: self.content_scopes,
|
2020-07-15 12:37:19 +01:00
|
|
|
options: self.options,
|
2020-07-25 19:22:12 -04:00
|
|
|
modules: self.modules,
|
2020-08-06 21:00:34 -04:00
|
|
|
module_config: self.module_config,
|
2020-07-08 10:15:54 -04:00
|
|
|
}
|
|
|
|
.parse_stmt();
|
2020-08-18 00:17:06 -04:00
|
|
|
self.scopes.exit_scope();
|
|
|
|
return tmp;
|
2020-07-08 10:15:54 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
return Err(("expected \"{\".", tok.pos()).into());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
self.whitespace();
|
|
|
|
|
|
|
|
Ok(body)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(super) fn parse_for(&mut self) -> SassResult<Vec<Stmt>> {
|
2020-08-07 11:39:14 -04:00
|
|
|
self.whitespace_or_comment();
|
2020-08-06 21:36:11 -04:00
|
|
|
self.expect_char('$')?;
|
|
|
|
|
|
|
|
let var = self
|
|
|
|
.parse_identifier_no_interpolation(false)?
|
|
|
|
.map_node(|n| n.into());
|
|
|
|
|
2020-08-07 11:39:14 -04:00
|
|
|
self.whitespace_or_comment();
|
2020-07-08 10:15:54 -04:00
|
|
|
self.span_before = match self.toks.peek() {
|
|
|
|
Some(tok) => tok.pos,
|
|
|
|
None => return Err(("Expected \"from\".", var.span).into()),
|
|
|
|
};
|
|
|
|
if self.parse_identifier()?.node.to_ascii_lowercase() != "from" {
|
|
|
|
return Err(("Expected \"from\".", var.span).into());
|
|
|
|
}
|
2020-08-07 11:39:14 -04:00
|
|
|
self.whitespace_or_comment();
|
2020-08-07 17:33:30 -04:00
|
|
|
|
2020-08-08 14:36:59 -04:00
|
|
|
let from_val = self.parse_value(false, &|toks| match toks.peek() {
|
|
|
|
Some(Token { kind: 't', pos })
|
|
|
|
| Some(Token { kind: 'T', pos })
|
|
|
|
| Some(Token { kind: '\\', pos }) => {
|
|
|
|
let span = *pos;
|
|
|
|
let mut ident = match peek_ident_no_interpolation(toks, false, span) {
|
|
|
|
Ok(s) => s,
|
|
|
|
Err(..) => return false,
|
|
|
|
};
|
|
|
|
ident.node.make_ascii_lowercase();
|
|
|
|
let v = match ident.node.to_ascii_lowercase().as_str() {
|
|
|
|
"to" | "through" => true,
|
|
|
|
_ => false,
|
|
|
|
};
|
|
|
|
toks.reset_cursor();
|
|
|
|
v
|
|
|
|
}
|
|
|
|
Some(..) | None => false,
|
|
|
|
})?;
|
2020-08-07 17:33:30 -04:00
|
|
|
|
|
|
|
let through = if self.scan_identifier("through")? {
|
|
|
|
1
|
|
|
|
} else if self.scan_identifier("to")? {
|
|
|
|
0
|
|
|
|
} else {
|
|
|
|
return Err(("Expected \"to\" or \"through\".", self.span_before).into());
|
|
|
|
};
|
|
|
|
|
2020-07-08 10:15:54 -04:00
|
|
|
let from = match from_val.node {
|
2020-08-07 16:21:15 -04:00
|
|
|
Value::Dimension(Some(n), ..) => match n.to_i32() {
|
2020-08-07 16:51:17 -04:00
|
|
|
Some(std::i32::MAX) | Some(std::i32::MIN) | None => {
|
2020-08-07 16:21:15 -04:00
|
|
|
return Err((format!("{} is not an int.", n), from_val.span).into())
|
|
|
|
}
|
2020-07-08 10:15:54 -04:00
|
|
|
Some(v) => v,
|
|
|
|
},
|
2020-08-07 16:21:15 -04:00
|
|
|
Value::Dimension(None, ..) => return Err(("NaN is not an int.", from_val.span).into()),
|
2020-07-08 10:15:54 -04:00
|
|
|
v => {
|
|
|
|
return Err((
|
2020-08-07 16:21:15 -04:00
|
|
|
format!("{} is not a number.", v.inspect(from_val.span)?),
|
2020-07-08 10:15:54 -04:00
|
|
|
from_val.span,
|
|
|
|
)
|
|
|
|
.into())
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-08-08 14:36:59 -04:00
|
|
|
let to_val = self.parse_value(true, &|_| false)?;
|
2020-07-08 10:15:54 -04:00
|
|
|
let to = match to_val.node {
|
2020-08-07 16:21:15 -04:00
|
|
|
Value::Dimension(Some(n), ..) => match n.to_i32() {
|
2020-08-07 16:51:17 -04:00
|
|
|
Some(std::i32::MAX) | Some(std::i32::MIN) | None => {
|
2020-08-07 16:21:15 -04:00
|
|
|
return Err((format!("{} is not an int.", n), to_val.span).into())
|
|
|
|
}
|
2020-07-08 10:15:54 -04:00
|
|
|
Some(v) => v,
|
|
|
|
},
|
2020-08-07 16:21:15 -04:00
|
|
|
Value::Dimension(None, ..) => return Err(("NaN is not an int.", from_val.span).into()),
|
2020-07-08 10:15:54 -04:00
|
|
|
v => {
|
|
|
|
return Err((
|
2020-08-07 16:21:15 -04:00
|
|
|
format!("{} is not a number.", v.to_css_string(to_val.span)?),
|
2020-07-08 10:15:54 -04:00
|
|
|
to_val.span,
|
|
|
|
)
|
|
|
|
.into())
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-08-06 21:36:11 -04:00
|
|
|
self.expect_char('{')?;
|
2020-07-08 10:15:54 -04:00
|
|
|
|
|
|
|
let body = read_until_closing_curly_brace(self.toks)?;
|
2020-08-07 16:21:15 -04:00
|
|
|
|
|
|
|
self.expect_char('}')?;
|
2020-07-08 10:15:54 -04:00
|
|
|
|
|
|
|
let (mut x, mut y);
|
|
|
|
// we can't use an inclusive range here
|
|
|
|
#[allow(clippy::range_plus_one)]
|
2020-08-07 16:21:15 -04:00
|
|
|
let iter: &mut dyn Iterator<Item = i32> = if from < to {
|
2020-07-08 10:15:54 -04:00
|
|
|
x = from..(to + through);
|
|
|
|
&mut x
|
|
|
|
} else {
|
|
|
|
y = ((to - through)..(from + 1)).skip(1).rev();
|
|
|
|
&mut y
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut stmts = Vec::new();
|
|
|
|
|
2020-07-08 14:51:04 -04:00
|
|
|
self.scopes.enter_new_scope();
|
2020-07-08 10:15:54 -04:00
|
|
|
|
|
|
|
for i in iter {
|
2020-07-08 23:12:03 -04:00
|
|
|
self.scopes.insert_var_last(
|
2020-07-08 22:38:56 -04:00
|
|
|
var.node,
|
2020-07-27 18:55:38 -04:00
|
|
|
Value::Dimension(Some(Number::from(i)), Unit::None, true),
|
2020-07-08 10:15:54 -04:00
|
|
|
);
|
|
|
|
if self.flags.in_function() {
|
|
|
|
let these_stmts = Parser {
|
|
|
|
toks: &mut body.clone().into_iter().peekmore(),
|
|
|
|
map: self.map,
|
|
|
|
path: self.path,
|
|
|
|
scopes: self.scopes,
|
|
|
|
global_scope: self.global_scope,
|
|
|
|
super_selectors: self.super_selectors,
|
|
|
|
span_before: self.span_before,
|
|
|
|
content: self.content,
|
|
|
|
flags: self.flags | ContextFlags::IN_CONTROL_FLOW,
|
|
|
|
at_root: self.at_root,
|
|
|
|
at_root_has_selector: self.at_root_has_selector,
|
|
|
|
extender: self.extender,
|
2020-07-08 17:52:37 -04:00
|
|
|
content_scopes: self.content_scopes,
|
2020-07-15 12:37:19 +01:00
|
|
|
options: self.options,
|
2020-07-25 19:22:12 -04:00
|
|
|
modules: self.modules,
|
2020-08-06 21:00:34 -04:00
|
|
|
module_config: self.module_config,
|
2020-07-08 10:15:54 -04:00
|
|
|
}
|
2020-07-25 19:22:12 -04:00
|
|
|
.parse_stmt()?;
|
2020-07-08 10:15:54 -04:00
|
|
|
if !these_stmts.is_empty() {
|
|
|
|
return Ok(these_stmts);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
stmts.append(
|
|
|
|
&mut Parser {
|
|
|
|
toks: &mut body.clone().into_iter().peekmore(),
|
|
|
|
map: self.map,
|
|
|
|
path: self.path,
|
|
|
|
scopes: self.scopes,
|
|
|
|
global_scope: self.global_scope,
|
|
|
|
super_selectors: self.super_selectors,
|
|
|
|
span_before: self.span_before,
|
|
|
|
content: self.content,
|
|
|
|
flags: self.flags | ContextFlags::IN_CONTROL_FLOW,
|
|
|
|
at_root: self.at_root,
|
|
|
|
at_root_has_selector: self.at_root_has_selector,
|
|
|
|
extender: self.extender,
|
2020-07-08 17:52:37 -04:00
|
|
|
content_scopes: self.content_scopes,
|
2020-07-15 12:37:19 +01:00
|
|
|
options: self.options,
|
2020-07-25 19:22:12 -04:00
|
|
|
modules: self.modules,
|
2020-08-06 21:00:34 -04:00
|
|
|
module_config: self.module_config,
|
2020-07-08 10:15:54 -04:00
|
|
|
}
|
2020-07-25 19:22:12 -04:00
|
|
|
.parse_stmt()?,
|
2020-07-08 10:15:54 -04:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-08 14:51:04 -04:00
|
|
|
self.scopes.exit_scope();
|
2020-07-08 10:15:54 -04:00
|
|
|
|
|
|
|
Ok(stmts)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(super) fn parse_while(&mut self) -> SassResult<Vec<Stmt>> {
|
2020-08-07 11:39:14 -04:00
|
|
|
// technically not necessary to eat whitespace here, but since we
|
|
|
|
// operate on raw tokens rather than an AST, it potentially saves a lot of
|
|
|
|
// time in re-parsing
|
|
|
|
self.whitespace_or_comment();
|
2020-07-08 10:15:54 -04:00
|
|
|
let cond = read_until_open_curly_brace(self.toks)?;
|
|
|
|
|
|
|
|
if cond.is_empty() {
|
|
|
|
return Err(("Expected expression.", self.span_before).into());
|
|
|
|
}
|
|
|
|
|
|
|
|
self.toks.next();
|
|
|
|
|
|
|
|
let mut body = read_until_closing_curly_brace(self.toks)?;
|
|
|
|
|
|
|
|
body.push(match self.toks.next() {
|
|
|
|
Some(tok) => tok,
|
|
|
|
None => return Err(("expected \"}\".", self.span_before).into()),
|
|
|
|
});
|
|
|
|
|
|
|
|
let mut stmts = Vec::new();
|
2020-07-09 23:52:35 -04:00
|
|
|
let mut val = self.parse_value_from_vec(cond.clone(), true)?;
|
2020-07-08 14:51:04 -04:00
|
|
|
self.scopes.enter_new_scope();
|
2020-07-08 10:15:54 -04:00
|
|
|
while val.node.is_true() {
|
|
|
|
if self.flags.in_function() {
|
|
|
|
let these_stmts = Parser {
|
|
|
|
toks: &mut body.clone().into_iter().peekmore(),
|
|
|
|
map: self.map,
|
|
|
|
path: self.path,
|
|
|
|
scopes: self.scopes,
|
|
|
|
global_scope: self.global_scope,
|
|
|
|
super_selectors: self.super_selectors,
|
|
|
|
span_before: self.span_before,
|
|
|
|
content: self.content,
|
|
|
|
flags: self.flags | ContextFlags::IN_CONTROL_FLOW,
|
|
|
|
at_root: self.at_root,
|
|
|
|
at_root_has_selector: self.at_root_has_selector,
|
|
|
|
extender: self.extender,
|
2020-07-08 17:52:37 -04:00
|
|
|
content_scopes: self.content_scopes,
|
2020-07-15 12:37:19 +01:00
|
|
|
options: self.options,
|
2020-07-25 19:22:12 -04:00
|
|
|
modules: self.modules,
|
2020-08-06 21:00:34 -04:00
|
|
|
module_config: self.module_config,
|
2020-07-08 10:15:54 -04:00
|
|
|
}
|
2020-07-25 19:22:12 -04:00
|
|
|
.parse_stmt()?;
|
2020-07-08 10:15:54 -04:00
|
|
|
if !these_stmts.is_empty() {
|
|
|
|
return Ok(these_stmts);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
stmts.append(
|
|
|
|
&mut Parser {
|
|
|
|
toks: &mut body.clone().into_iter().peekmore(),
|
|
|
|
map: self.map,
|
|
|
|
path: self.path,
|
|
|
|
scopes: self.scopes,
|
|
|
|
global_scope: self.global_scope,
|
|
|
|
super_selectors: self.super_selectors,
|
|
|
|
span_before: self.span_before,
|
|
|
|
content: self.content,
|
|
|
|
flags: self.flags | ContextFlags::IN_CONTROL_FLOW,
|
|
|
|
at_root: self.at_root,
|
|
|
|
at_root_has_selector: self.at_root_has_selector,
|
|
|
|
extender: self.extender,
|
2020-07-08 17:52:37 -04:00
|
|
|
content_scopes: self.content_scopes,
|
2020-07-15 12:37:19 +01:00
|
|
|
options: self.options,
|
2020-07-25 19:22:12 -04:00
|
|
|
modules: self.modules,
|
2020-08-06 21:00:34 -04:00
|
|
|
module_config: self.module_config,
|
2020-07-08 10:15:54 -04:00
|
|
|
}
|
2020-07-25 19:22:12 -04:00
|
|
|
.parse_stmt()?,
|
2020-07-08 10:15:54 -04:00
|
|
|
);
|
|
|
|
}
|
2020-07-09 23:52:35 -04:00
|
|
|
val = self.parse_value_from_vec(cond.clone(), true)?;
|
2020-07-08 10:15:54 -04:00
|
|
|
}
|
2020-07-08 14:51:04 -04:00
|
|
|
self.scopes.exit_scope();
|
2020-07-08 10:15:54 -04:00
|
|
|
|
|
|
|
Ok(stmts)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(super) fn parse_each(&mut self) -> SassResult<Vec<Stmt>> {
|
|
|
|
let mut vars: Vec<Spanned<Identifier>> = Vec::new();
|
|
|
|
|
2020-08-07 11:39:14 -04:00
|
|
|
self.whitespace_or_comment();
|
2020-07-08 10:15:54 -04:00
|
|
|
loop {
|
2020-08-06 21:36:11 -04:00
|
|
|
self.expect_char('$')?;
|
2020-07-08 10:15:54 -04:00
|
|
|
|
2020-08-06 21:36:11 -04:00
|
|
|
vars.push(self.parse_identifier()?.map_node(|i| i.into()));
|
|
|
|
|
2020-08-07 11:39:14 -04:00
|
|
|
self.whitespace_or_comment();
|
2020-07-08 10:15:54 -04:00
|
|
|
if self
|
|
|
|
.toks
|
|
|
|
.peek()
|
|
|
|
.ok_or(("expected \"$\".", vars[vars.len() - 1].span))?
|
|
|
|
.kind
|
|
|
|
== ','
|
|
|
|
{
|
|
|
|
self.toks.next();
|
2020-08-07 11:39:14 -04:00
|
|
|
self.whitespace_or_comment();
|
2020-07-08 10:15:54 -04:00
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let i = self.parse_identifier()?;
|
|
|
|
if i.node.to_ascii_lowercase() != "in" {
|
|
|
|
return Err(("Expected \"in\".", i.span).into());
|
|
|
|
}
|
2020-08-07 11:39:14 -04:00
|
|
|
self.whitespace_or_comment();
|
2020-07-08 10:15:54 -04:00
|
|
|
let iter_val_toks = read_until_open_curly_brace(self.toks)?;
|
2020-07-09 23:52:35 -04:00
|
|
|
let iter = self
|
|
|
|
.parse_value_from_vec(iter_val_toks, true)?
|
|
|
|
.node
|
|
|
|
.as_list();
|
2020-07-08 10:15:54 -04:00
|
|
|
self.toks.next();
|
|
|
|
self.whitespace();
|
|
|
|
let mut body = read_until_closing_curly_brace(self.toks)?;
|
|
|
|
body.push(match self.toks.next() {
|
|
|
|
Some(tok) => tok,
|
|
|
|
None => return Err(("expected \"}\".", self.span_before).into()),
|
|
|
|
});
|
|
|
|
self.whitespace();
|
|
|
|
|
|
|
|
let mut stmts = Vec::new();
|
|
|
|
|
2020-07-08 23:12:03 -04:00
|
|
|
self.scopes.enter_new_scope();
|
|
|
|
|
2020-07-08 10:15:54 -04:00
|
|
|
for row in iter {
|
|
|
|
if vars.len() == 1 {
|
2020-07-27 18:55:38 -04:00
|
|
|
self.scopes.insert_var_last(vars[0].node, row);
|
2020-07-08 10:15:54 -04:00
|
|
|
} else {
|
|
|
|
for (var, val) in vars.iter().zip(
|
|
|
|
row.as_list()
|
|
|
|
.into_iter()
|
|
|
|
.chain(std::iter::once(Value::Null).cycle()),
|
|
|
|
) {
|
2020-07-27 18:55:38 -04:00
|
|
|
self.scopes.insert_var_last(var.node, val);
|
2020-07-08 10:15:54 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if self.flags.in_function() {
|
|
|
|
let these_stmts = Parser {
|
|
|
|
toks: &mut body.clone().into_iter().peekmore(),
|
|
|
|
map: self.map,
|
|
|
|
path: self.path,
|
|
|
|
scopes: self.scopes,
|
|
|
|
global_scope: self.global_scope,
|
|
|
|
super_selectors: self.super_selectors,
|
|
|
|
span_before: self.span_before,
|
|
|
|
content: self.content,
|
|
|
|
flags: self.flags | ContextFlags::IN_CONTROL_FLOW,
|
|
|
|
at_root: self.at_root,
|
|
|
|
at_root_has_selector: self.at_root_has_selector,
|
|
|
|
extender: self.extender,
|
2020-07-08 17:52:37 -04:00
|
|
|
content_scopes: self.content_scopes,
|
2020-07-15 12:37:19 +01:00
|
|
|
options: self.options,
|
2020-07-25 19:22:12 -04:00
|
|
|
modules: self.modules,
|
2020-08-06 21:00:34 -04:00
|
|
|
module_config: self.module_config,
|
2020-07-08 10:15:54 -04:00
|
|
|
}
|
2020-07-25 19:22:12 -04:00
|
|
|
.parse_stmt()?;
|
2020-07-08 10:15:54 -04:00
|
|
|
if !these_stmts.is_empty() {
|
|
|
|
return Ok(these_stmts);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
stmts.append(
|
|
|
|
&mut Parser {
|
|
|
|
toks: &mut body.clone().into_iter().peekmore(),
|
|
|
|
map: self.map,
|
|
|
|
path: self.path,
|
|
|
|
scopes: self.scopes,
|
|
|
|
global_scope: self.global_scope,
|
|
|
|
super_selectors: self.super_selectors,
|
|
|
|
span_before: self.span_before,
|
|
|
|
content: self.content,
|
|
|
|
flags: self.flags | ContextFlags::IN_CONTROL_FLOW,
|
|
|
|
at_root: self.at_root,
|
|
|
|
at_root_has_selector: self.at_root_has_selector,
|
|
|
|
extender: self.extender,
|
2020-07-08 17:52:37 -04:00
|
|
|
content_scopes: self.content_scopes,
|
2020-07-15 12:37:19 +01:00
|
|
|
options: self.options,
|
2020-07-25 19:22:12 -04:00
|
|
|
modules: self.modules,
|
2020-08-06 21:00:34 -04:00
|
|
|
module_config: self.module_config,
|
2020-07-08 10:15:54 -04:00
|
|
|
}
|
2020-07-25 19:22:12 -04:00
|
|
|
.parse_stmt()?,
|
2020-07-08 10:15:54 -04:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-08 23:12:03 -04:00
|
|
|
self.scopes.exit_scope();
|
|
|
|
|
2020-07-08 10:15:54 -04:00
|
|
|
Ok(stmts)
|
|
|
|
}
|
|
|
|
}
|