From 2969f08e43562c8ad05286f30c2ce9d0215413a3 Mon Sep 17 00:00:00 2001 From: Connor Skees Date: Tue, 20 Jul 2021 11:04:14 -0400 Subject: [PATCH] simplify lookahead for `@for` --- src/lexer.rs | 4 -- src/parse/control_flow.rs | 21 +++--- src/utils/mod.rs | 2 - src/utils/peek_until.rs | 134 -------------------------------------- tests/for.rs | 46 +++++++++++++ 5 files changed, 58 insertions(+), 149 deletions(-) delete mode 100644 src/utils/peek_until.rs diff --git a/src/lexer.rs b/src/lexer.rs index c11006a..5d95592 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -26,10 +26,6 @@ impl Lexer { self.amt_peeked = 0; } - pub fn advance_cursor(&mut self) { - self.amt_peeked += 1; - } - pub fn peek_next(&mut self) -> Option { self.amt_peeked += 1; diff --git a/src/parse/control_flow.rs b/src/parse/control_flow.rs index a9684a6..be74169 100644 --- a/src/parse/control_flow.rs +++ b/src/parse/control_flow.rs @@ -7,9 +7,7 @@ use crate::{ lexer::Lexer, parse::{ContextFlags, Parser, Stmt}, unit::Unit, - utils::{ - peek_ident_no_interpolation, read_until_closing_curly_brace, read_until_open_curly_brace, - }, + utils::{read_until_closing_curly_brace, read_until_open_curly_brace}, value::{Number, Value}, Token, }; @@ -177,17 +175,22 @@ impl<'a> Parser<'a> { self.whitespace_or_comment(); let from_val = self.parse_value(false, &|parser| match parser.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(parser.toks, false, span) { + Some(Token { kind: 't', .. }) + | Some(Token { kind: 'T', .. }) + | Some(Token { kind: '\\', .. }) => { + let start = parser.toks.cursor(); + + let mut ident = match parser.parse_identifier_no_interpolation(false) { Ok(s) => s, Err(..) => return false, }; + ident.node.make_ascii_lowercase(); + let v = matches!(ident.node.to_ascii_lowercase().as_str(), "to" | "through"); - parser.toks.reset_cursor(); + + parser.toks.set_cursor(start); + v } Some(..) | None => false, diff --git a/src/utils/mod.rs b/src/utils/mod.rs index f8317ad..94a47de 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,13 +1,11 @@ pub(crate) use chars::*; pub(crate) use comment_whitespace::*; pub(crate) use number::*; -pub(crate) use peek_until::*; pub(crate) use read_until::*; pub(crate) use strings::*; mod chars; mod comment_whitespace; mod number; -mod peek_until; mod read_until; mod strings; diff --git a/src/utils/peek_until.rs b/src/utils/peek_until.rs deleted file mode 100644 index 8550970..0000000 --- a/src/utils/peek_until.rs +++ /dev/null @@ -1,134 +0,0 @@ -use codemap::{Span, Spanned}; - -use crate::{error::SassResult, lexer::Lexer, Token}; - -use super::{as_hex, hex_char_for, is_name, is_name_start}; - -pub(crate) fn peek_escape(toks: &mut Lexer) -> SassResult { - let mut value = 0; - let first = match toks.peek() { - Some(t) => t, - None => return Ok(String::new()), - }; - let mut span = first.pos; - if first.kind == '\n' { - return Err(("Expected escape sequence.", first.pos()).into()); - } else if first.kind.is_ascii_hexdigit() { - for _ in 0..6 { - let next = match toks.peek() { - Some(t) => t, - None => break, - }; - if !next.kind.is_ascii_hexdigit() { - break; - } - value *= 16; - value += as_hex(next.kind); - span = span.merge(next.pos); - toks.peek_forward(1); - } - if toks.peek().is_some() && toks.peek().unwrap().kind.is_whitespace() { - toks.peek_forward(1); - } - } else { - value = first.kind as u32; - toks.advance_cursor(); - } - - let c = std::char::from_u32(value).ok_or(("Invalid escape sequence.", span))?; - if is_name(c) { - Ok(c.to_string()) - } else if value <= 0x1F || value == 0x7F { - let mut buf = String::with_capacity(4); - buf.push('\\'); - if value > 0xF { - buf.push(hex_char_for(value >> 4)); - } - buf.push(hex_char_for(value & 0xF)); - buf.push(' '); - Ok(buf) - } else { - Ok(format!("\\{}", c)) - } -} - -pub(crate) fn peek_ident_no_interpolation( - toks: &mut Lexer, - unit: bool, - span_before: Span, -) -> SassResult> { - let mut span = toks - .peek() - .ok_or(("Expected identifier.", span_before))? - .pos(); - let mut text = String::new(); - if let Some(Token { kind: '-', .. }) = toks.peek() { - toks.peek_forward(1); - text.push('-'); - if toks.peek().is_none() { - return Ok(Spanned { node: text, span }); - } - if let Some(Token { kind: '-', .. }) = toks.peek() { - toks.peek_forward(1); - text.push('-'); - text.push_str(&peek_ident_body_no_interpolation(toks, unit, span)?.node); - return Ok(Spanned { node: text, span }); - } - } - - let first = match toks.peek() { - Some(v) => v, - None => return Err(("Expected identifier.", span).into()), - }; - - if is_name_start(first.kind) { - text.push(first.kind); - toks.peek_forward(1); - } else if first.kind == '\\' { - toks.peek_forward(1); - text.push_str(&peek_escape(toks)?); - } else { - return Err(("Expected identifier.", first.pos()).into()); - } - - let body = peek_ident_body_no_interpolation(toks, unit, span)?; - span = span.merge(body.span); - text.push_str(&body.node); - Ok(Spanned { node: text, span }) -} - -fn peek_ident_body_no_interpolation( - toks: &mut Lexer, - unit: bool, - mut span: Span, -) -> SassResult> { - let mut text = String::new(); - while let Some(tok) = toks.peek() { - span = span.merge(tok.pos()); - if unit && tok.kind == '-' { - // Disallow `-` followed by a dot or a digit digit in units. - let second = match toks.peek_forward(1) { - Some(v) => v, - None => break, - }; - - toks.peek_backward(1).unwrap(); - - if second.kind == '.' || second.kind.is_ascii_digit() { - break; - } - toks.peek_forward(1); - text.push('-'); - text.push(toks.peek_forward(1).unwrap().kind); - } else if is_name(tok.kind) { - text.push(tok.kind); - toks.peek_forward(1); - } else if tok.kind == '\\' { - toks.peek_forward(1); - text.push_str(&peek_escape(toks)?); - } else { - break; - } - } - Ok(Spanned { node: text, span }) -} diff --git a/tests/for.rs b/tests/for.rs index b10fedd..0062006 100644 --- a/tests/for.rs +++ b/tests/for.rs @@ -123,6 +123,52 @@ test!( }", "@foo;\n@foo;\n" ); +test!( + escaped_keyword_through, + r"@for $i from 0 \74 hrough 0 { + a { + color: \74; + } + }", + "a {\n color: t;\n}\n" +); +test!( + escaped_keyword_to, + r"@for $i from 0 \74 o 1 { + a { + color: \74; + } + }", + "a {\n color: t;\n}\n" +); +test!( + escaped_keyword_from_lower, + r"@for $i \66rom 0 to 1 { + a { + color: \74; + } + }", + "a {\n color: t;\n}\n" +); +test!( + escaped_keyword_from_upper, + r"@for $i \46rom 0 to 1 { + a { + color: \74; + } + }", + "a {\n color: t;\n}\n" +); +test!( + variable_name_is_keyword, + r"$to: 0; + @for $from from $to to 1 { + a { + color: red; + } + }", + "a {\n color: red;\n}\n" +); error!( missing_keyword_from, "@for $i fro", "Error: Expected \"from\"."