From 817c808826d60d7a1806c55a6464f0b132774fec Mon Sep 17 00:00:00 2001 From: Connor Skees Date: Tue, 7 Jul 2020 15:36:10 -0400 Subject: [PATCH] dont allocate `@if` body unless necessary --- src/parse/common.rs | 14 +---- src/parse/mod.rs | 131 ++++++++++++++++++++++++-------------------- 2 files changed, 73 insertions(+), 72 deletions(-) diff --git a/src/parse/common.rs b/src/parse/common.rs index 11d31a3..5ebc75d 100644 --- a/src/parse/common.rs +++ b/src/parse/common.rs @@ -5,7 +5,7 @@ use std::{ use codemap::Spanned; -use crate::{value::Value, Token}; +use crate::value::Value; #[derive(Debug, Clone)] pub(crate) struct NeverEmptyVec { @@ -67,18 +67,6 @@ pub(super) enum SelectorOrStyle { Style(String, Option>>), } -#[derive(Debug, Clone)] -pub(super) struct Branch { - pub cond: Vec, - pub toks: Vec, -} - -impl Branch { - pub fn new(cond: Vec, toks: Vec) -> Branch { - Branch { cond, toks } - } -} - #[derive(Debug, Copy, Clone)] pub(crate) struct ContextFlags(u8); diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 35ad39e..2da005b 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -25,7 +25,7 @@ use crate::{ {Cow, Token}, }; -use common::{Branch, ContextFlags, NeverEmptyVec, SelectorOrStyle}; +use common::{ContextFlags, NeverEmptyVec, SelectorOrStyle}; pub(crate) use value::{HigherIntermediateValue, ValueVisitor}; @@ -510,29 +510,52 @@ 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> { 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)?; if init_cond_toks.is_empty() { return Err(("Expected expression.", self.span_before).into()); } + // consume the open curly brace let span_before = match self.toks.next() { Some(t) => t.pos, None => return Err(("Expected expression.", self.span_before).into()), }; self.whitespace_or_comment(); - let mut init_toks = read_until_closing_curly_brace(self.toks)?; - if let Some(tok) = self.toks.next() { - init_toks.push(tok); + + if self.parse_value_from_vec(init_cond_toks)?.is_true() { + 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 { - return Err(("expected \"}\".", span_before).into()); + self.throw_away_until_closing_curly_brace(); } - self.whitespace(); - branches.push(Branch::new(init_cond_toks, init_toks)); - - let mut else_ = Vec::new(); + self.whitespace_or_comment(); loop { if let Some(Token { kind: '@', pos }) = self.toks.peek().cloned() { @@ -542,34 +565,43 @@ impl<'a> Parser<'a> { self.toks.reset_cursor(); break; } - self.toks.take(4).for_each(drop); + self.toks.truncate_iterator_to_cursor(); } else { break; } self.whitespace(); - if let Some(tok) = self.toks.next() { - self.whitespace(); - match tok.kind.to_ascii_lowercase() { + if let Some(tok) = self.toks.peek().cloned() { + match tok.kind { 'i' if matches!( - self.toks.peek(), + self.toks.peek_forward(1), Some(Token { kind: 'f', .. }) | Some(Token { kind: 'F', .. }) ) => { + self.toks.next(); self.toks.next(); let cond = read_until_open_curly_brace(self.toks)?; + // todo: ensure there is a `{` self.toks.next(); - self.whitespace(); - branches.push(Branch::new( - cond, - read_until_closing_curly_brace(self.toks)?, - )); - self.toks.next(); + if !found_true && self.parse_value_from_vec(cond)?.is_true() { + found_true = true; + body = read_until_closing_curly_brace(self.toks)?; + // todo: ensure there is a `{` + self.toks.next(); + } else { + self.throw_away_until_closing_curly_brace(); + } self.whitespace(); } '{' => { - else_ = read_until_closing_curly_brace(self.toks)?; 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()); @@ -581,44 +613,25 @@ impl<'a> Parser<'a> { } self.whitespace(); - for branch in branches { - self.span_before = branch.cond.first().unwrap().pos; - if self.parse_value_from_vec(branch.cond)?.node.is_true() { - return Parser { - toks: &mut branch.toks.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(); + if found_true { + Parser { + toks: &mut body.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() + } 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> {