simplify lookahead for @for

This commit is contained in:
Connor Skees 2021-07-20 11:04:14 -04:00
parent fd685ee36f
commit 2969f08e43
5 changed files with 58 additions and 149 deletions

View File

@ -26,10 +26,6 @@ impl Lexer {
self.amt_peeked = 0; self.amt_peeked = 0;
} }
pub fn advance_cursor(&mut self) {
self.amt_peeked += 1;
}
pub fn peek_next(&mut self) -> Option<Token> { pub fn peek_next(&mut self) -> Option<Token> {
self.amt_peeked += 1; self.amt_peeked += 1;

View File

@ -7,9 +7,7 @@ use crate::{
lexer::Lexer, lexer::Lexer,
parse::{ContextFlags, Parser, Stmt}, parse::{ContextFlags, Parser, Stmt},
unit::Unit, unit::Unit,
utils::{ utils::{read_until_closing_curly_brace, read_until_open_curly_brace},
peek_ident_no_interpolation, read_until_closing_curly_brace, read_until_open_curly_brace,
},
value::{Number, Value}, value::{Number, Value},
Token, Token,
}; };
@ -177,17 +175,22 @@ impl<'a> Parser<'a> {
self.whitespace_or_comment(); self.whitespace_or_comment();
let from_val = self.parse_value(false, &|parser| match parser.toks.peek() { let from_val = self.parse_value(false, &|parser| match parser.toks.peek() {
Some(Token { kind: 't', pos }) Some(Token { kind: 't', .. })
| Some(Token { kind: 'T', pos }) | Some(Token { kind: 'T', .. })
| Some(Token { kind: '\\', pos }) => { | Some(Token { kind: '\\', .. }) => {
let span = pos; let start = parser.toks.cursor();
let mut ident = match peek_ident_no_interpolation(parser.toks, false, span) {
let mut ident = match parser.parse_identifier_no_interpolation(false) {
Ok(s) => s, Ok(s) => s,
Err(..) => return false, Err(..) => return false,
}; };
ident.node.make_ascii_lowercase(); ident.node.make_ascii_lowercase();
let v = matches!(ident.node.to_ascii_lowercase().as_str(), "to" | "through"); let v = matches!(ident.node.to_ascii_lowercase().as_str(), "to" | "through");
parser.toks.reset_cursor();
parser.toks.set_cursor(start);
v v
} }
Some(..) | None => false, Some(..) | None => false,

View File

@ -1,13 +1,11 @@
pub(crate) use chars::*; pub(crate) use chars::*;
pub(crate) use comment_whitespace::*; pub(crate) use comment_whitespace::*;
pub(crate) use number::*; pub(crate) use number::*;
pub(crate) use peek_until::*;
pub(crate) use read_until::*; pub(crate) use read_until::*;
pub(crate) use strings::*; pub(crate) use strings::*;
mod chars; mod chars;
mod comment_whitespace; mod comment_whitespace;
mod number; mod number;
mod peek_until;
mod read_until; mod read_until;
mod strings; mod strings;

View File

@ -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<String> {
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<Spanned<String>> {
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<Spanned<String>> {
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 })
}

View File

@ -123,6 +123,52 @@ test!(
}", }",
"@foo;\n@foo;\n" "@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!( error!(
missing_keyword_from, missing_keyword_from,
"@for $i fro", "Error: Expected \"from\"." "@for $i fro", "Error: Expected \"from\"."