grass/src/utils.rs

843 lines
24 KiB
Rust
Raw Normal View History

2020-03-01 14:53:52 -05:00
use std::iter::{Iterator, Peekable};
2020-04-19 20:22:31 -04:00
use codemap::{Span, Spanned};
2020-04-12 19:37:12 -04:00
2020-03-29 13:28:17 -04:00
use crate::common::QuoteKind;
2020-02-16 10:54:25 -05:00
use crate::error::SassResult;
2020-03-01 12:03:14 -05:00
use crate::selector::Selector;
2020-01-26 09:28:44 -05:00
use crate::value::Value;
2020-03-29 13:28:17 -04:00
use crate::{Scope, Token};
pub(crate) trait IsWhitespace {
fn is_whitespace(&self) -> bool;
}
2020-04-03 13:28:37 -04:00
impl IsWhitespace for char {
fn is_whitespace(&self) -> bool {
self.is_ascii_whitespace()
}
}
2020-01-20 16:00:37 -05:00
pub(crate) fn devour_whitespace<I: Iterator<Item = W>, W: IsWhitespace>(
s: &mut Peekable<I>,
) -> bool {
let mut found_whitespace = false;
while let Some(w) = s.peek() {
if !w.is_whitespace() {
break;
}
found_whitespace = true;
s.next();
}
found_whitespace
}
pub(crate) trait IsComment {
fn is_comment(&self) -> bool;
}
2020-03-29 13:28:17 -04:00
pub(crate) fn devour_whitespace_or_comment<I: Iterator<Item = Token>>(
2020-04-12 19:37:12 -04:00
toks: &mut Peekable<I>,
2020-03-29 13:28:17 -04:00
) -> SassResult<bool> {
let mut found_whitespace = false;
2020-04-12 19:37:12 -04:00
while let Some(tok) = toks.peek() {
if tok.kind == '/' {
let pos = toks.next().unwrap().pos();
match toks.peek().unwrap().kind {
2020-03-29 13:28:17 -04:00
'*' => {
2020-04-12 19:37:12 -04:00
eat_comment(toks, &Scope::new(), &Selector::new())?;
2020-03-29 13:28:17 -04:00
}
2020-04-12 19:37:12 -04:00
'/' => read_until_newline(toks),
_ => return Err(("Expected expression.", pos).into()),
2020-03-29 13:28:17 -04:00
};
found_whitespace = true;
continue;
}
2020-04-12 19:37:12 -04:00
if !tok.is_whitespace() {
break;
}
found_whitespace = true;
2020-04-12 19:37:12 -04:00
toks.next();
}
2020-03-29 13:28:17 -04:00
Ok(found_whitespace)
}
2020-02-24 17:08:49 -05:00
pub(crate) fn parse_interpolation<I: Iterator<Item = Token>>(
2020-03-30 02:54:11 -04:00
toks: &mut Peekable<I>,
scope: &Scope,
2020-03-01 12:03:14 -05:00
super_selector: &Selector,
2020-04-12 19:37:12 -04:00
) -> SassResult<Spanned<Value>> {
2020-04-06 00:11:18 -04:00
let val = Value::from_vec(read_until_closing_curly_brace(toks), scope, super_selector)?;
2020-03-30 02:54:11 -04:00
toks.next();
2020-04-12 19:37:12 -04:00
Ok(Spanned {
node: val.node.eval(val.span)?.node.unquote(),
span: val.span,
})
}
2020-01-29 21:02:32 -05:00
pub(crate) struct VariableDecl {
2020-04-12 19:37:12 -04:00
pub val: Spanned<Value>,
2020-01-29 21:02:32 -05:00
pub default: bool,
pub global: bool,
2020-01-29 21:02:32 -05:00
}
impl VariableDecl {
2020-04-12 19:37:12 -04:00
pub const fn new(val: Spanned<Value>, default: bool, global: bool) -> VariableDecl {
VariableDecl {
val,
default,
global,
}
2020-01-29 21:02:32 -05:00
}
}
2020-03-24 22:13:38 -04:00
// Eat tokens until an open curly brace
//
// Does not consume the open curly brace
pub(crate) fn read_until_open_curly_brace<I: Iterator<Item = Token>>(
toks: &mut Peekable<I>,
) -> Vec<Token> {
let mut val = Vec::new();
let mut n = 0;
while let Some(tok) = toks.peek() {
match tok.kind {
2020-03-29 13:28:17 -04:00
'{' => n += 1,
'}' => n -= 1,
'/' => {
let next = toks.next().unwrap();
match toks.peek().unwrap().kind {
'/' => read_until_newline(toks),
_ => val.push(next),
};
continue;
}
2020-03-24 22:13:38 -04:00
_ => {}
}
if n == 1 {
break;
}
2020-03-24 22:13:38 -04:00
val.push(toks.next().unwrap());
}
val
}
pub(crate) fn read_until_closing_curly_brace<I: Iterator<Item = Token>>(
toks: &mut Peekable<I>,
) -> Vec<Token> {
let mut t = Vec::new();
let mut nesting = 0;
while let Some(tok) = toks.peek() {
match tok.kind {
2020-03-29 13:28:17 -04:00
q @ '"' | q @ '\'' => {
t.push(toks.next().unwrap());
t.extend(read_until_closing_quote(toks, q));
2020-03-24 22:13:38 -04:00
}
2020-03-29 13:28:17 -04:00
'{' => {
2020-03-24 22:13:38 -04:00
nesting += 1;
t.push(toks.next().unwrap());
}
2020-03-29 13:28:17 -04:00
'}' => {
2020-03-24 22:13:38 -04:00
if nesting == 0 {
break;
} else {
nesting -= 1;
t.push(toks.next().unwrap());
}
}
'/' => {
let next = toks.next().unwrap();
match toks.peek().unwrap().kind {
'/' => read_until_newline(toks),
_ => t.push(next),
};
continue;
}
2020-03-24 22:13:38 -04:00
_ => t.push(toks.next().unwrap()),
}
}
devour_whitespace(toks);
t
}
2020-03-23 16:29:55 -04:00
pub(crate) fn read_until_closing_quote<I: Iterator<Item = Token>>(
2020-01-14 19:34:13 -05:00
toks: &mut Peekable<I>,
2020-03-29 13:28:17 -04:00
q: char,
2020-03-23 16:29:55 -04:00
) -> Vec<Token> {
let mut t = Vec::new();
while let Some(tok) = toks.next() {
2020-03-23 16:29:55 -04:00
match tok.kind {
'"' if q == '"' => {
2020-03-23 16:29:55 -04:00
t.push(tok);
break;
}
'\'' if q == '\'' => {
2020-03-23 16:29:55 -04:00
t.push(tok);
break;
}
2020-03-29 13:28:17 -04:00
'\\' => {
2020-03-23 16:29:55 -04:00
t.push(tok);
t.push(toks.next().unwrap());
2020-03-23 16:29:55 -04:00
}
'#' => {
2020-03-29 13:28:17 -04:00
t.push(tok);
let next = toks.peek().unwrap();
if next.kind == '{' {
t.push(toks.next().unwrap());
t.append(&mut read_until_closing_curly_brace(toks));
}
2020-03-29 13:28:17 -04:00
}
2020-03-23 16:29:55 -04:00
_ => t.push(tok),
}
}
t
}
2020-03-29 13:28:17 -04:00
pub(crate) fn read_until_semicolon_or_closing_curly_brace<I: Iterator<Item = Token>>(
2020-03-23 16:29:55 -04:00
toks: &mut Peekable<I>,
) -> Vec<Token> {
let mut t = Vec::new();
2020-01-29 21:02:32 -05:00
let mut nesting = 0;
while let Some(tok) = toks.peek() {
match tok.kind {
2020-03-29 13:28:17 -04:00
';' => {
2020-01-29 21:02:32 -05:00
break;
}
2020-03-29 23:00:39 -04:00
'\\' => {
t.push(toks.next().unwrap());
t.push(toks.next().unwrap());
}
2020-03-29 13:28:17 -04:00
'"' | '\'' => {
2020-03-23 16:29:55 -04:00
let quote = toks.next().unwrap();
t.push(quote.clone());
2020-03-29 13:28:17 -04:00
t.extend(read_until_closing_quote(toks, quote.kind));
}
2020-03-29 13:28:17 -04:00
'{' => {
2020-01-29 21:02:32 -05:00
nesting += 1;
2020-03-23 16:29:55 -04:00
t.push(toks.next().unwrap());
2020-01-29 21:02:32 -05:00
}
2020-03-29 13:28:17 -04:00
'}' => {
if nesting == 0 {
break;
} else {
nesting -= 1;
t.push(toks.next().unwrap());
}
}
'/' => {
let next = toks.next().unwrap();
match toks.peek().unwrap().kind {
'/' => read_until_newline(toks),
_ => t.push(next),
};
continue;
}
2020-03-29 13:28:17 -04:00
_ => t.push(toks.next().unwrap()),
}
}
devour_whitespace(toks);
t
}
pub(crate) fn read_until_semicolon_or_open_or_closing_curly_brace<I: Iterator<Item = Token>>(
toks: &mut Peekable<I>,
) -> Vec<Token> {
let mut t = Vec::new();
let mut nesting = 0;
while let Some(tok) = toks.peek() {
match tok.kind {
';' => {
break;
}
2020-03-29 23:00:39 -04:00
'\\' => {
t.push(toks.next().unwrap());
t.push(toks.next().unwrap());
}
2020-03-29 13:28:17 -04:00
'"' | '\'' => {
let quote = toks.next().unwrap();
t.push(quote.clone());
t.extend(read_until_closing_quote(toks, quote.kind));
}
'#' => {
t.push(toks.next().unwrap());
match toks.peek().unwrap().kind {
'{' => nesting += 1,
';' => break,
'}' => {
if nesting == 0 {
break;
} else {
nesting -= 1;
}
}
_ => {}
}
t.push(toks.next().unwrap());
}
'{' => break,
'}' => {
2020-01-29 21:02:32 -05:00
if nesting == 0 {
break;
} else {
nesting -= 1;
2020-03-23 16:29:55 -04:00
t.push(toks.next().unwrap());
2020-01-29 21:02:32 -05:00
}
}
'/' => {
let next = toks.next().unwrap();
match toks.peek().unwrap().kind {
'/' => read_until_newline(toks),
_ => t.push(next),
};
continue;
}
2020-03-23 16:29:55 -04:00
_ => t.push(toks.next().unwrap()),
2020-01-29 21:02:32 -05:00
}
}
2020-01-14 19:34:13 -05:00
devour_whitespace(toks);
2020-03-23 16:29:55 -04:00
t
}
pub(crate) fn eat_variable_value<I: Iterator<Item = Token>>(
toks: &mut Peekable<I>,
scope: &Scope,
super_selector: &Selector,
) -> SassResult<VariableDecl> {
devour_whitespace(toks);
let mut default = false;
let mut global = false;
2020-03-29 13:28:17 -04:00
let mut raw = read_until_semicolon_or_closing_curly_brace(toks)
2020-03-23 16:29:55 -04:00
.into_iter()
.peekable();
2020-03-29 23:00:39 -04:00
if toks.peek().is_some() && toks.peek().unwrap().kind == ';' {
2020-03-29 13:28:17 -04:00
toks.next();
}
2020-04-06 00:11:18 -04:00
let mut val_toks = Vec::new();
2020-03-29 13:28:17 -04:00
while let Some(tok) = raw.next() {
match tok.kind {
'!' => {
let next = raw.next().unwrap();
match next.kind {
'i' => todo!("!important"),
'g' => {
2020-04-12 19:37:12 -04:00
let s = eat_ident(&mut raw, scope, super_selector)?;
if s.node.to_ascii_lowercase().as_str() == "lobal" {
2020-03-29 13:28:17 -04:00
global = true;
} else {
2020-04-12 19:37:12 -04:00
return Err(("Invalid flag name.", s.span).into());
2020-03-29 13:28:17 -04:00
}
}
'd' => {
2020-04-12 19:37:12 -04:00
let s = eat_ident(&mut raw, scope, super_selector)?;
if s.to_ascii_lowercase().as_str() == "efault" {
2020-03-29 13:28:17 -04:00
default = true;
} else {
2020-04-12 19:37:12 -04:00
return Err(("Invalid flag name.", s.span).into());
2020-03-29 13:28:17 -04:00
}
}
2020-04-12 19:37:12 -04:00
_ => return Err(("Invalid flag name.", next.pos()).into()),
2020-03-29 13:28:17 -04:00
}
}
2020-04-06 00:11:18 -04:00
_ => val_toks.push(tok),
2020-03-29 13:28:17 -04:00
}
}
devour_whitespace(toks);
2020-04-06 00:11:18 -04:00
let val = Value::from_vec(val_toks, scope, super_selector)?;
Ok(VariableDecl::new(val, default, global))
2020-01-14 19:34:13 -05:00
}
2020-04-19 20:22:31 -04:00
fn ident_body<I: Iterator<Item = Token>>(
toks: &mut Peekable<I>,
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 == '-' {
todo!()
// Disallow `-` followed by a dot or a digit digit in units.
// var second = scanner.peekChar(1);
// if (second != null && (second == $dot || isDigit(second))) break;
// text.writeCharCode(scanner.readChar());
} else if is_name(tok.kind) {
text.push(toks.next().unwrap().kind);
} else if tok.kind == '\\' {
toks.next();
text.push_str(&escape(toks, false)?);
} else {
break;
}
}
Ok(Spanned { node: text, span })
}
fn interpolated_ident_body<I: Iterator<Item = Token>>(
toks: &mut Peekable<I>,
scope: &Scope,
2020-03-01 12:03:14 -05:00
super_selector: &Selector,
2020-04-19 20:22:31 -04:00
mut span: Span,
2020-04-12 19:37:12 -04:00
) -> SassResult<Spanned<String>> {
2020-04-19 20:22:31 -04:00
let mut buf = String::new();
while let Some(tok) = toks.peek() {
2020-04-19 20:22:31 -04:00
if tok.kind == '_'
|| tok.kind.is_alphanumeric()
|| tok.kind == '-'
|| tok.kind as u32 >= 0x0080
{
span = span.merge(tok.pos());
buf.push(toks.next().unwrap().kind);
} else if tok.kind == '\\' {
toks.next();
buf.push_str(&escape(toks, false)?);
} else if tok.kind == '#' {
toks.next();
let next = toks.next().unwrap();
if next.kind == '{' {
let interpolation = parse_interpolation(toks, scope, super_selector)?;
buf.push_str(&interpolation.node.to_css_string(interpolation.span)?);
}
2020-04-19 20:22:31 -04:00
} else {
break;
2020-03-29 13:28:17 -04:00
}
}
2020-04-19 20:22:31 -04:00
Ok(Spanned { node: buf, span })
2020-03-29 13:28:17 -04:00
}
2020-04-19 20:22:31 -04:00
fn is_name(c: char) -> bool {
is_name_start(c) || c.is_digit(10) || c == '-'
}
fn is_name_start(c: char) -> bool {
// NOTE: in the dart-sass implementation, identifiers cannot start
// with numbers. We explicitly differentiate from the reference
// implementation here in order to support selectors beginning with numbers.
// This can be considered a hack and in the future it would be nice to refactor
// how this is handled.
c == '_' || c.is_alphanumeric() || c as u32 >= 0x0080
}
fn escape<I: Iterator<Item = Token>>(
2020-03-29 13:28:17 -04:00
toks: &mut Peekable<I>,
2020-04-19 20:22:31 -04:00
identifier_start: bool,
) -> SassResult<String> {
let mut value = 0;
let first = match toks.peek() {
Some(t) => t,
None => return Ok(String::new()),
2020-04-12 19:37:12 -04:00
};
2020-04-19 20:22:31 -04:00
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() {
2020-03-29 13:28:17 -04:00
break;
}
2020-04-19 20:22:31 -04:00
value *= 16;
value += as_hex(toks.next().unwrap().kind as u32)
}
if toks.peek().is_some() && toks.peek().unwrap().kind.is_whitespace() {
toks.next();
}
} else {
value = toks.next().unwrap().kind as u32;
}
// tabs are emitted literally
// TODO: figure out where this check is done
// in the source dart
if value == 0x9 {
return Ok("\\\t".to_string());
}
let c = std::char::from_u32(value).unwrap();
if (identifier_start && is_name_start(c) && !c.is_digit(10))
|| (!identifier_start && is_name(c))
{
Ok(c.to_string())
} else if value <= 0x1F || value == 0x7F || (identifier_start && c.is_digit(10)) {
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 eat_ident<I: Iterator<Item = Token>>(
toks: &mut Peekable<I>,
scope: &Scope,
super_selector: &Selector,
) -> SassResult<Spanned<String>> {
// TODO: take span as param because we use unwrap here
let mut span = toks.peek().unwrap().pos();
let mut text = String::new();
if toks.peek().unwrap().kind == '-' {
toks.next();
text.push('-');
if toks.peek().unwrap().kind == '-' {
toks.next();
text.push('-');
text.push_str(&interpolated_ident_body(toks, scope, super_selector, span)?.node);
return Ok(Spanned { node: text, span });
}
}
2020-04-19 20:22:31 -04:00
let Token { kind: first, pos } = match toks.peek() {
Some(v) => *v,
None => return Err(("Expected identifier.", span).into()),
};
if is_name_start(first) {
text.push(toks.next().unwrap().kind);
} else if first == '\\' {
toks.next();
text.push_str(&escape(toks, true)?);
// TODO: peekmore
// (first == '#' && scanner.peekChar(1) == $lbrace)
} else if first == '#' {
toks.next();
if toks.peek().is_none() {
return Err(("Expected identifier.", pos).into());
}
let Token { kind, pos } = toks.peek().unwrap();
if kind == &'{' {
toks.next();
text.push_str(
&parse_interpolation(toks, scope, super_selector)?
.node
.to_css_string(span)?,
);
} else {
return Err(("Expected identifier.", *pos).into());
}
} else {
return Err(("Expected identifier.", pos).into());
}
let body = interpolated_ident_body(toks, scope, super_selector, pos)?;
span = span.merge(body.span);
text.push_str(&body.node);
Ok(Spanned { node: text, span })
}
pub(crate) fn eat_ident_no_interpolation<I: Iterator<Item = Token>>(
toks: &mut Peekable<I>,
) -> SassResult<Spanned<String>> {
let mut span = toks.peek().unwrap().pos();
let mut text = String::new();
if toks.peek().unwrap().kind == '-' {
toks.next();
text.push('-');
if toks.peek().unwrap().kind == '-' {
toks.next();
text.push('-');
text.push_str(&ident_body(toks, false, 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(toks.next().unwrap().kind);
} else if first.kind == '\\' {
toks.next();
text.push_str(&escape(toks, true)?);
} else {
return Err(("Expected identifier.", first.pos()).into());
}
let body = ident_body(toks, false, span)?;
span = span.merge(body.span);
text.push_str(&body.node);
Ok(Spanned { node: text, span })
}
2020-04-12 19:37:12 -04:00
pub(crate) fn eat_number<I: Iterator<Item = Token>>(
toks: &mut Peekable<I>,
) -> SassResult<Spanned<String>> {
2020-03-29 13:28:17 -04:00
let mut whole = String::new();
2020-04-12 19:37:12 -04:00
let mut span = if let Some(tok) = toks.peek() {
tok.pos()
} else {
todo!()
};
2020-03-29 13:28:17 -04:00
while let Some(c) = toks.peek() {
if !c.kind.is_numeric() {
break;
}
let tok = toks.next().unwrap();
2020-04-12 19:37:12 -04:00
span = span.merge(tok.pos());
2020-03-29 13:28:17 -04:00
whole.push(tok.kind);
}
if toks.peek().is_none() {
2020-04-12 19:37:12 -04:00
return Ok(Spanned { node: whole, span });
2020-03-29 13:28:17 -04:00
}
let mut dec = String::new();
2020-04-12 19:37:12 -04:00
let next_tok = toks.peek().unwrap().clone();
if next_tok.kind == '.' {
2020-03-29 13:28:17 -04:00
toks.next();
dec.push('.');
while let Some(c) = toks.peek() {
if !c.kind.is_numeric() {
break;
}
let tok = toks.next().unwrap();
2020-04-12 19:37:12 -04:00
span = span.merge(tok.pos());
2020-03-29 13:28:17 -04:00
dec.push(tok.kind);
}
}
if dec.len() == 1 {
2020-04-12 19:37:12 -04:00
return Err(("Expected digit.", next_tok.pos()).into());
2020-03-29 13:28:17 -04:00
}
whole.push_str(&dec);
2020-04-12 19:37:12 -04:00
Ok(Spanned { node: whole, span })
2020-03-29 13:28:17 -04:00
}
/// Eat tokens until a newline
///
/// This exists largely to eat silent comments, "//"
/// We only have to check for \n as the lexing step normalizes all newline characters
///
/// The newline is consumed
pub(crate) fn read_until_newline<I: Iterator<Item = Token>>(toks: &mut Peekable<I>) {
2020-03-30 17:06:23 -04:00
for tok in toks {
2020-03-29 13:28:17 -04:00
if tok.kind == '\n' {
break;
}
}
}
/// Eat and return the contents of a comment.
///
/// This function assumes that the starting "/*" has already been consumed
/// The entirety of the comment, including the ending "*/" is consumed.
/// Note that the ending "*/" is not included in the output.
pub(crate) fn eat_comment<I: Iterator<Item = Token>>(
toks: &mut Peekable<I>,
2020-04-18 21:01:12 -04:00
scope: &Scope,
super_selector: &Selector,
2020-04-12 19:37:12 -04:00
) -> SassResult<Spanned<String>> {
2020-03-29 13:28:17 -04:00
let mut comment = String::new();
2020-04-12 19:37:12 -04:00
let mut span = if let Some(tok) = toks.peek() {
tok.pos()
} else {
todo!()
};
2020-03-29 13:28:17 -04:00
while let Some(tok) = toks.next() {
2020-04-12 19:37:12 -04:00
span = span.merge(tok.pos());
2020-03-30 17:06:23 -04:00
if tok.kind == '*' && toks.peek().unwrap().kind == '/' {
toks.next();
break;
2020-04-18 21:01:12 -04:00
} else if tok.kind == '#' && toks.peek().unwrap().kind == '{' {
toks.next();
comment
.push_str(&parse_interpolation(toks, scope, super_selector)?.to_css_string(span)?);
continue;
2020-03-29 13:28:17 -04:00
}
comment.push(tok.kind);
}
devour_whitespace(toks);
2020-04-12 19:37:12 -04:00
Ok(Spanned {
node: comment,
span,
})
2020-03-29 13:28:17 -04:00
}
fn as_hex(c: u32) -> u32 {
if c <= '9' as u32 {
c - '0' as u32
} else if c <= 'F' as u32 {
10 + c - 'A' as u32
} else {
10 + c - 'a' as u32
}
}
pub(crate) fn parse_quoted_string<I: Iterator<Item = Token>>(
toks: &mut Peekable<I>,
scope: &Scope,
2020-03-29 13:28:17 -04:00
q: char,
2020-03-01 12:03:14 -05:00
super_selector: &Selector,
2020-04-12 19:37:12 -04:00
) -> SassResult<Spanned<Value>> {
let mut s = String::new();
2020-04-12 19:37:12 -04:00
let mut span = if let Some(tok) = toks.peek() {
tok.pos()
} else {
todo!()
};
while let Some(tok) = toks.next() {
2020-04-12 19:37:12 -04:00
span = span.merge(tok.pos());
match tok.kind {
'"' if q == '"' => break,
'\'' if q == '\'' => break,
'#' => {
2020-03-29 13:28:17 -04:00
if toks.peek().unwrap().kind == '{' {
toks.next();
2020-04-12 19:37:12 -04:00
let interpolation = parse_interpolation(toks, scope, super_selector)?;
s.push_str(&interpolation.node.to_css_string(interpolation.span)?);
2020-03-29 13:28:17 -04:00
continue;
} else {
s.push('#');
2020-03-30 02:54:11 -04:00
continue;
2020-03-29 13:28:17 -04:00
}
}
2020-04-12 19:37:12 -04:00
'\n' => return Err(("Expected \".", tok.pos()).into()),
'\\' => {
let first = match toks.peek() {
Some(c) => c,
None => {
s.push('\u{FFFD}');
continue;
2020-03-29 13:28:17 -04:00
}
};
if first.kind == '\n' {
return Err(("Expected escape sequence.", first.pos()).into());
2020-03-29 13:28:17 -04:00
}
if first.kind.is_ascii_hexdigit() {
let mut value = 0;
for _ in 0..6 {
let next = match toks.peek() {
Some(c) => c,
None => break,
};
if !next.kind.is_ascii_hexdigit() {
break;
}
value = (value << 4) + as_hex(toks.next().unwrap().kind as u32);
}
if toks.peek().is_some() && toks.peek().unwrap().kind.is_ascii_whitespace() {
toks.next();
}
if value == 0 || (value >= 0xD800 && value <= 0xDFFF) || value >= 0x10FFFF {
s.push('\u{FFFD}');
} else {
2020-04-19 20:22:31 -04:00
s.push(std::char::from_u32(value).unwrap());
}
2020-03-29 13:28:17 -04:00
} else {
s.push(toks.next().unwrap().kind);
2020-03-29 13:28:17 -04:00
}
}
_ => s.push(tok.kind),
}
}
2020-04-12 19:37:12 -04:00
Ok(Spanned {
2020-04-18 13:19:30 -04:00
node: Value::Ident(s, QuoteKind::Quoted),
2020-04-12 19:37:12 -04:00
span,
})
}
2020-04-01 15:32:52 -04:00
pub(crate) fn read_until_closing_paren<I: Iterator<Item = Token>>(
toks: &mut Peekable<I>,
) -> Vec<Token> {
let mut v = Vec::new();
let mut scope = 0;
while let Some(tok) = toks.next() {
match tok.kind {
')' => {
if scope < 1 {
v.push(tok);
return v;
} else {
scope -= 1;
}
}
'(' => scope += 1,
'"' | '\'' => {
v.push(tok.clone());
v.extend(read_until_closing_quote(toks, tok.kind));
continue;
}
_ => {}
}
v.push(tok)
}
v
}
pub(crate) fn read_until_closing_square_brace<I: Iterator<Item = Token>>(
toks: &mut Peekable<I>,
) -> Vec<Token> {
let mut v = Vec::new();
let mut scope = 0;
while let Some(tok) = toks.next() {
match tok.kind {
']' => {
if scope < 1 {
v.push(tok);
return v;
} else {
scope -= 1;
}
}
'[' => scope += 1,
'"' | '\'' => {
v.push(tok.clone());
v.extend(read_until_closing_quote(toks, tok.kind));
continue;
}
_ => {}
}
v.push(tok)
}
v
}
pub(crate) fn read_until_char<I: Iterator<Item = Token>>(
toks: &mut Peekable<I>,
c: char,
) -> Vec<Token> {
let mut v = Vec::new();
while let Some(tok) = toks.next() {
match tok.kind {
'"' | '\'' => {
v.push(tok.clone());
v.extend(read_until_closing_quote(toks, tok.kind));
continue;
}
t if t == c => break,
_ => {}
}
v.push(tok)
}
v
}
2020-04-02 21:44:26 -04:00
pub(crate) fn is_ident_char(c: char) -> bool {
c.is_ascii_alphabetic() || c == '_' || c == '\\' || (!c.is_ascii() && !c.is_control())
}
2020-04-19 15:50:22 -04:00
pub(crate) fn hex_char_for(number: u32) -> char {
assert!(number < 0x10);
std::char::from_u32(if number < 0xA {
0x30 + number
} else {
0x61 - 0xA + number
})
.unwrap()
}