dont allocate @if body unless necessary

This commit is contained in:
Connor Skees 2020-07-07 15:36:10 -04:00
parent 69089a13cf
commit 817c808826
2 changed files with 73 additions and 72 deletions

View File

@ -5,7 +5,7 @@ use std::{
use codemap::Spanned; use codemap::Spanned;
use crate::{value::Value, Token}; use crate::value::Value;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct NeverEmptyVec<T> { pub(crate) struct NeverEmptyVec<T> {
@ -67,18 +67,6 @@ pub(super) enum SelectorOrStyle {
Style(String, Option<Box<Spanned<Value>>>), Style(String, Option<Box<Spanned<Value>>>),
} }
#[derive(Debug, Clone)]
pub(super) struct Branch {
pub cond: Vec<Token>,
pub toks: Vec<Token>,
}
impl Branch {
pub fn new(cond: Vec<Token>, toks: Vec<Token>) -> Branch {
Branch { cond, toks }
}
}
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub(crate) struct ContextFlags(u8); pub(crate) struct ContextFlags(u8);

View File

@ -25,7 +25,7 @@ use crate::{
{Cow, Token}, {Cow, Token},
}; };
use common::{Branch, ContextFlags, NeverEmptyVec, SelectorOrStyle}; use common::{ContextFlags, NeverEmptyVec, SelectorOrStyle};
pub(crate) use value::{HigherIntermediateValue, ValueVisitor}; pub(crate) use value::{HigherIntermediateValue, ValueVisitor};
@ -510,29 +510,52 @@ impl<'a> Parser<'a> {
} }
impl<'a> Parser<'a> { impl<'a> Parser<'a> {
fn throw_away_until_closing_curly_brace(&mut self) {
let mut scope = 0;
while let Some(tok) = self.toks.next() {
match tok.kind {
'}' => if scope == 0 {
break
} else {
scope -= 1;
}
'{' => scope += 1,
_ => continue,
}
}
}
fn parse_if(&mut self) -> SassResult<Vec<Stmt>> { fn parse_if(&mut self) -> SassResult<Vec<Stmt>> {
self.whitespace_or_comment(); self.whitespace_or_comment();
let mut branches = Vec::new();
let mut found_true = false;
let mut body = Vec::new();
let init_cond_toks = read_until_open_curly_brace(self.toks)?; let init_cond_toks = read_until_open_curly_brace(self.toks)?;
if init_cond_toks.is_empty() { if init_cond_toks.is_empty() {
return Err(("Expected expression.", self.span_before).into()); return Err(("Expected expression.", self.span_before).into());
} }
// consume the open curly brace
let span_before = match self.toks.next() { let span_before = match self.toks.next() {
Some(t) => t.pos, Some(t) => t.pos,
None => return Err(("Expected expression.", self.span_before).into()), None => return Err(("Expected expression.", self.span_before).into()),
}; };
self.whitespace_or_comment(); self.whitespace_or_comment();
let mut init_toks = read_until_closing_curly_brace(self.toks)?;
if let Some(tok) = self.toks.next() { if self.parse_value_from_vec(init_cond_toks)?.is_true() {
init_toks.push(tok); found_true = true;
let mut init_toks = read_until_closing_curly_brace(self.toks)?;
if let Some(tok) = self.toks.next() {
init_toks.push(tok);
} else {
return Err(("expected \"}\".", span_before).into());
}
body = init_toks;
} else { } else {
return Err(("expected \"}\".", span_before).into()); self.throw_away_until_closing_curly_brace();
} }
self.whitespace();
branches.push(Branch::new(init_cond_toks, init_toks)); self.whitespace_or_comment();
let mut else_ = Vec::new();
loop { loop {
if let Some(Token { kind: '@', pos }) = self.toks.peek().cloned() { if let Some(Token { kind: '@', pos }) = self.toks.peek().cloned() {
@ -542,34 +565,43 @@ impl<'a> Parser<'a> {
self.toks.reset_cursor(); self.toks.reset_cursor();
break; break;
} }
self.toks.take(4).for_each(drop); self.toks.truncate_iterator_to_cursor();
} else { } else {
break; break;
} }
self.whitespace(); self.whitespace();
if let Some(tok) = self.toks.next() { if let Some(tok) = self.toks.peek().cloned() {
self.whitespace(); match tok.kind {
match tok.kind.to_ascii_lowercase() {
'i' if matches!( 'i' if matches!(
self.toks.peek(), self.toks.peek_forward(1),
Some(Token { kind: 'f', .. }) | Some(Token { kind: 'F', .. }) Some(Token { kind: 'f', .. }) | Some(Token { kind: 'F', .. })
) => ) =>
{ {
self.toks.next();
self.toks.next(); self.toks.next();
let cond = read_until_open_curly_brace(self.toks)?; let cond = read_until_open_curly_brace(self.toks)?;
// todo: ensure there is a `{`
self.toks.next(); self.toks.next();
self.whitespace(); if !found_true && self.parse_value_from_vec(cond)?.is_true() {
branches.push(Branch::new( found_true = true;
cond, body = read_until_closing_curly_brace(self.toks)?;
read_until_closing_curly_brace(self.toks)?, // todo: ensure there is a `{`
)); self.toks.next();
self.toks.next(); } else {
self.throw_away_until_closing_curly_brace();
}
self.whitespace(); self.whitespace();
} }
'{' => { '{' => {
else_ = read_until_closing_curly_brace(self.toks)?;
self.toks.next(); self.toks.next();
break; if !found_true {
found_true = true;
body = read_until_closing_curly_brace(self.toks)?;
self.toks.next();
break;
} else {
self.throw_away_until_closing_curly_brace();
}
} }
_ => { _ => {
return Err(("expected \"{\".", tok.pos()).into()); return Err(("expected \"{\".", tok.pos()).into());
@ -581,44 +613,25 @@ impl<'a> Parser<'a> {
} }
self.whitespace(); self.whitespace();
for branch in branches { if found_true {
self.span_before = branch.cond.first().unwrap().pos; Parser {
if self.parse_value_from_vec(branch.cond)?.node.is_true() { toks: &mut body.into_iter().peekmore(),
return Parser { map: self.map,
toks: &mut branch.toks.into_iter().peekmore(), path: self.path,
map: self.map, scopes: self.scopes,
path: self.path, global_scope: self.global_scope,
scopes: self.scopes, super_selectors: self.super_selectors,
global_scope: self.global_scope, span_before: self.span_before,
super_selectors: self.super_selectors, content: self.content,
span_before: self.span_before, flags: self.flags | ContextFlags::IN_CONTROL_FLOW,
content: self.content, at_root: self.at_root,
flags: self.flags | ContextFlags::IN_CONTROL_FLOW, at_root_has_selector: self.at_root_has_selector,
at_root: self.at_root, extender: self.extender,
at_root_has_selector: self.at_root_has_selector,
extender: self.extender,
}
.parse();
} }
.parse()
} else {
Ok(Vec::new())
} }
if else_.is_empty() {
return Ok(Vec::new());
}
Parser {
toks: &mut else_.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,
}
.parse()
} }
fn parse_for(&mut self) -> SassResult<Vec<Stmt>> { fn parse_for(&mut self) -> SassResult<Vec<Stmt>> {