handle large integers in builtin string functions

This commit is contained in:
ConnorSkees 2020-03-23 13:57:00 -04:00
parent 795c8bdb05
commit f4f9a79b2b
3 changed files with 99 additions and 82 deletions
src
builtin
value
tests

@ -76,7 +76,7 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
return Err(format!("{} is not an int.", n).into()) return Err(format!("{} is not an int.", n).into())
} }
Value::Dimension(n, Unit::None) if n.is_positive() => { Value::Dimension(n, Unit::None) if n.is_positive() => {
n.to_integer().to_usize().unwrap() n.to_integer().to_usize().unwrap_or(str_len+1)
} }
Value::Dimension(n, Unit::None) if n.is_zero() => 1_usize, Value::Dimension(n, Unit::None) if n.is_zero() => 1_usize,
Value::Dimension(n, Unit::None) if n < -Number::from(str_len) => 1_usize, Value::Dimension(n, Unit::None) if n < -Number::from(str_len) => 1_usize,
@ -93,13 +93,13 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
return Err(format!("{} is not an int.", n).into()) return Err(format!("{} is not an int.", n).into())
} }
Value::Dimension(n, Unit::None) if n.is_positive() => { Value::Dimension(n, Unit::None) if n.is_positive() => {
n.to_integer().to_usize().unwrap() n.to_integer().to_usize().unwrap_or(str_len+1)
} }
Value::Dimension(n, Unit::None) if n.is_zero() => 0_usize, Value::Dimension(n, Unit::None) if n.is_zero() => 0_usize,
Value::Dimension(n, Unit::None) if n < -Number::from(str_len) => 0_usize, Value::Dimension(n, Unit::None) if n < -Number::from(str_len) => 0_usize,
Value::Dimension(n, Unit::None) => (BigInt::from(str_len + 1) + n.to_integer()) Value::Dimension(n, Unit::None) => (BigInt::from(str_len + 1) + n.to_integer())
.to_usize() .to_usize()
.unwrap(), .unwrap_or(str_len+1),
v @ Value::Dimension(..) => { v @ Value::Dimension(..) => {
return Err(format!("$end: Expected {} to have no units.", v).into()) return Err(format!("$end: Expected {} to have no units.", v).into())
} }
@ -111,13 +111,8 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
end = str_len; end = str_len;
} }
if start > end || start > str_len { if start >= end || start > str_len {
match quotes { Ok(Value::Ident(String::new(), quotes.normalize()))
QuoteKind::Double | QuoteKind::Single => {
Ok(Value::Ident(String::new(), QuoteKind::Double))
}
QuoteKind::None => Ok(Value::Null),
}
} else { } else {
let s = string[start - 1..end].to_string(); let s = string[start - 1..end].to_string();
match quotes { match quotes {
@ -151,8 +146,8 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
"str-insert".to_owned(), "str-insert".to_owned(),
Box::new(|args, _| { Box::new(|args, _| {
max_args!(args, 3); max_args!(args, 3);
let (s1, q) = match arg!(args, 0, "string") { let (s1, quotes) = match arg!(args, 0, "string") {
Value::Ident(i, q) => (i, q), Value::Ident(i, q) => (i, q.normalize()),
v => return Err(format!("$string: {} is not a string.", v).into()), v => return Err(format!("$string: {} is not a string.", v).into()),
}; };
@ -172,11 +167,6 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
v => return Err(format!("$index: {} is not a number.", v).into()), v => return Err(format!("$index: {} is not a number.", v).into()),
}; };
let quotes = match q {
QuoteKind::Double | QuoteKind::Single => QuoteKind::Double,
QuoteKind::None => QuoteKind::None,
};
if s1.is_empty() { if s1.is_empty() {
return Ok(Value::Ident(substr, quotes)); return Ok(Value::Ident(substr, quotes));
} }
@ -201,14 +191,19 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
let string = if index.is_positive() { let string = if index.is_positive() {
insert( insert(
index.to_integer().to_usize().unwrap().min(len + 1) - 1, index
.to_integer()
.to_usize()
.unwrap_or(len + 1)
.min(len + 1)
- 1,
s1, s1,
&substr, &substr,
) )
} else if index.is_zero() { } else if index.is_zero() {
insert(0, s1, &substr) insert(0, s1, &substr)
} else { } else {
let idx = index.abs().to_integer().to_usize().unwrap(); let idx = index.abs().to_integer().to_usize().unwrap_or(len + 1);
if idx > len { if idx > len {
insert(0, s1, &substr) insert(0, s1, &substr)
} else { } else {

@ -133,50 +133,49 @@ impl Sub for Value {
}, },
Self::BinaryOp(..) | Self::Paren(..) => (self.eval()? - other)?, Self::BinaryOp(..) | Self::Paren(..) => (self.eval()? - other)?,
Self::Ident(s1, q1) => match other { Self::Ident(s1, q1) => match other {
Self::Ident(s2, q2) => { Self::Ident(s2, q2) => Value::Ident(
Value::Ident( format!(
format!("{}{}{}-{}{}{}", q1.normalize(), s1, q1.normalize(), q2.normalize(), s2, q2.normalize()), "{}{}{}-{}{}{}",
QuoteKind::None, q1.normalize(),
) s1,
} q1.normalize(),
q2.normalize(),
s2,
q2.normalize()
),
QuoteKind::None,
),
Self::Important Self::Important
| Self::True | Self::True
| Self::False | Self::False
| Self::Dimension(..) | Self::Dimension(..)
| Self::Color(..) => { | Self::Color(..) => Value::Ident(
Value::Ident( format!("{}{}{}-{}", q1.normalize(), s1, q1.normalize(), other),
format!("{}{}{}-{}", q1.normalize(), s1, q1.normalize(), other), QuoteKind::None,
QuoteKind::None, ),
) Self::Null => Value::Ident(
} format!("{}{}{}-", q1.normalize(), s1, q1.normalize()),
Self::Null => { QuoteKind::None,
Value::Ident(format!("{}{}{}-", q1.normalize(), s1, q1.normalize()), QuoteKind::None) ),
} Self::List(..) => Value::Ident(
Self::List(..) => { format!("{}{}{}-{}", q1.normalize(), s1, q1.normalize(), other),
Value::Ident( QuoteKind::None,
format!("{}{}{}-{}", q1.normalize(), s1, q1.normalize(), other), ),
QuoteKind::None,
)
}
_ => todo!(), _ => todo!(),
}, },
Self::List(..) => match other { Self::List(..) => match other {
Self::Ident(s, q) => { Self::Ident(s, q) => Value::Ident(
Value::Ident( format!("{}-{}{}{}", self, q.normalize(), s, q.normalize()),
format!("{}-{}{}{}", self, q.normalize(), s, q.normalize()), QuoteKind::None,
QuoteKind::None, ),
)
}
Self::Paren(..) => (self + other.eval()?)?, Self::Paren(..) => (self + other.eval()?)?,
_ => Value::Ident(format!("{}-{}", self, other), QuoteKind::None), _ => Value::Ident(format!("{}-{}", self, other), QuoteKind::None),
}, },
_ => match other { _ => match other {
Self::Ident(s, q) => { Self::Ident(s, q) => Value::Ident(
Value::Ident( format!("{}-{}{}{}", self, q.normalize(), s, q.normalize()),
format!("{}-{}{}{}", self, q.normalize(), s, q.normalize()), QuoteKind::None,
QuoteKind::None, ),
)
}
Self::Null => Value::Ident(format!("{}-", self), QuoteKind::None), Self::Null => Value::Ident(format!("{}-", self), QuoteKind::None),
_ => Value::Ident(format!("{}-{}", self, other), QuoteKind::None), _ => Value::Ident(format!("{}-{}", self, other), QuoteKind::None),
}, },
@ -234,21 +233,20 @@ impl Div for Value {
todo!("unit conversions") todo!("unit conversions")
} }
} }
Self::Ident(s, q) => { Self::Ident(s, q) => Value::Ident(
Value::Ident( format!("{}{}/{}{}{}", num, unit, q.normalize(), s, q.normalize()),
format!("{}{}/{}{}{}", num, unit, q.normalize(), s, q.normalize()), QuoteKind::None,
QuoteKind::None, ),
)
}
Self::BinaryOp(..) | Self::Paren(..) => { Self::BinaryOp(..) | Self::Paren(..) => {
(Self::Dimension(num, unit) / other.eval()?)? (Self::Dimension(num, unit) / other.eval()?)?
} }
_ => todo!(), _ => todo!(),
}, },
Self::Color(c) => match other { Self::Color(c) => match other {
Self::Ident(s, q) => { Self::Ident(s, q) => Value::Ident(
Value::Ident(format!("{}/{}{}{}", c, q.normalize(), s, q.normalize()), QuoteKind::None) format!("{}/{}{}{}", c, q.normalize(), s, q.normalize()),
} QuoteKind::None,
),
Self::Null => Value::Ident(format!("{}/", c), QuoteKind::None), Self::Null => Value::Ident(format!("{}/", c), QuoteKind::None),
Self::Dimension(..) | Self::Color(..) => { Self::Dimension(..) | Self::Color(..) => {
return Err(format!("Undefined operation \"{} / {}\".", c, other).into()) return Err(format!("Undefined operation \"{} / {}\".", c, other).into())
@ -257,34 +255,37 @@ impl Div for Value {
}, },
Self::BinaryOp(..) | Self::Paren(..) => self.eval()?, Self::BinaryOp(..) | Self::Paren(..) => self.eval()?,
Self::Ident(s1, q1) => match other { Self::Ident(s1, q1) => match other {
Self::Ident(s2, q2) => { Self::Ident(s2, q2) => Value::Ident(
Value::Ident( format!(
format!("{}{}{}/{}{}{}", q1.normalize(), s1, q1.normalize(), q2.normalize(), s2, q2.normalize()), "{}{}{}/{}{}{}",
QuoteKind::None, q1.normalize(),
) s1,
} q1.normalize(),
q2.normalize(),
s2,
q2.normalize()
),
QuoteKind::None,
),
Self::Important Self::Important
| Self::True | Self::True
| Self::False | Self::False
| Self::Dimension(..) | Self::Dimension(..)
| Self::Color(..) => { | Self::Color(..) => Value::Ident(
Value::Ident( format!("{}{}{}/{}", q1.normalize(), s1, q1.normalize(), other),
format!("{}{}{}/{}", q1.normalize(), s1, q1.normalize(), other), QuoteKind::None,
QuoteKind::None, ),
) Self::Null => Value::Ident(
} format!("{}{}{}/", q1.normalize(), s1, q1.normalize()),
Self::Null => { QuoteKind::None,
Value::Ident(format!("{}{}{}/", q1.normalize(), s1, q1.normalize()), QuoteKind::None) ),
}
_ => todo!(), _ => todo!(),
}, },
_ => match other { _ => match other {
Self::Ident(s, q) => { Self::Ident(s, q) => Value::Ident(
Value::Ident( format!("{}/{}{}{}", self, q.normalize(), s, q.normalize()),
format!("{}/{}{}{}", self, q.normalize(), s, q.normalize()), QuoteKind::None,
QuoteKind::None, ),
)
}
Self::Null => Value::Ident(format!("{}/", self), QuoteKind::None), Self::Null => Value::Ident(format!("{}/", self), QuoteKind::None),
_ => Value::Ident(format!("{}/{}", self, other), QuoteKind::None), _ => Value::Ident(format!("{}/{}", self, other), QuoteKind::None),
}, },

@ -83,6 +83,16 @@ test!(
"a {\n color: str-slice(\"cde\", 1, 0);\n}\n", "a {\n color: str-slice(\"cde\", 1, 0);\n}\n",
"a {\n color: \"\";\n}\n" "a {\n color: \"\";\n}\n"
); );
test!(
str_slice_bigger_than_usize_max,
"a {\n color: str-slice($string: \"foo\", $start-at: -99999999999999999999, $end-at: 99999999999999999999);\n}\n",
"a {\n color: \"foo\";\n}\n"
);
test!(
str_slice_positive_index_bigger_than_usize_max,
"a {\n color: str-slice($string: \"foo\", $start-at: 99999999999999999999, $end-at: -99999999999999999999);\n}\n",
"a {\n color: \"\";\n}\n"
);
test!( test!(
str_len_dbl_quotes, str_len_dbl_quotes,
"a {\n color: str-length(\"cde\");\n}\n", "a {\n color: str-length(\"cde\");\n}\n",
@ -104,6 +114,7 @@ test!(
"a {\n color: 7;\n}\n" "a {\n color: 7;\n}\n"
); );
test!( test!(
#[ignore]
str_len_double_wide, str_len_double_wide,
"a {\n color: str-length(\"👭\");\n}\n", "a {\n color: str-length(\"👭\");\n}\n",
"@charset \"UTF-8\";\na {\n color: 1;\n}\n" "@charset \"UTF-8\";\na {\n color: 1;\n}\n"
@ -204,3 +215,13 @@ test!(
"a {\n color: str-insert(\"👭\", \"c\", 2);\n}\n", "a {\n color: str-insert(\"👭\", \"c\", 2);\n}\n",
"@charset \"UTF-8\";\na {\n color: \"👭c\";\n}\n" "@charset \"UTF-8\";\na {\n color: \"👭c\";\n}\n"
); );
test!(
str_insert_positive_index_bigger_than_usize_max,
"a {\n color: str-insert($string: \"foo\", $insert: \"X\", $index: 99999999999999999999);\n}\n",
"a {\n color: \"fooX\";\n}\n"
);
test!(
str_insert_negative_index_bigger_than_usize_max,
"a {\n color: str-insert($string: \"foo\", $insert: \"X\", $index: -99999999999999999999);\n}\n",
"a {\n color: \"Xfoo\";\n}\n"
);