1use super::css_function::css_function;
2use super::formalargs::call_args;
3use super::strings::{
4 name, sass_string_dq, sass_string_ext, sass_string_sq,
5 special_function_misc, special_url,
6};
7use super::unit::unit;
8use super::util::{ignore_comments, opt_spacelike, spacelike2};
9use super::{
10 input_to_string, list_or_single, position, sass_string, PResult, Span,
11};
12use crate::sass::{BinOp, SassString, Value};
13use crate::value::{ListSeparator, Numeric, Operator, Rgba};
14use nom::branch::alt;
15use nom::bytes::complete::{tag, tag_no_case};
16use nom::character::complete::{
17 alphanumeric1, char, digit1, multispace0, multispace1, one_of,
18};
19use nom::combinator::{
20 cut, into, map, map_opt, map_res, not, opt, peek, recognize, value,
21 verify,
22};
23use nom::error::context;
24use nom::multi::{fold_many0, many0, many_m_n, separated_list1};
25use nom::sequence::{delimited, pair, preceded, terminated};
26use nom::Parser as _;
27use std::str::from_utf8;
28
29pub fn value_expression(input: Span) -> PResult<Value> {
30 let (input, result) = separated_list1(
31 preceded(tag(","), ignore_comments),
32 terminated(slash_list, ignore_comments),
33 )
34 .parse(input)?;
35 let (input, trail) =
36 many0(delimited(opt_spacelike, tag(","), opt_spacelike))
37 .parse(input)?;
38 Ok((
39 input,
40 if result.len() == 1 && trail.is_empty() {
41 result.into_iter().next().unwrap()
42 } else {
43 Value::List(result, Some(ListSeparator::Comma), false)
44 },
45 ))
46}
47
48pub fn slash_list(input: Span) -> PResult<Value> {
49 let (rest, (first, space)) =
50 (space_list, ignore_comments).parse(input)?;
51
52 let (end, mut list) = fold_many0(
53 delimited(
54 (tag("/"), ignore_comments),
55 opt(space_list),
56 ignore_comments,
57 ),
58 Vec::new,
59 |mut acc, item| {
60 acc.push(item.unwrap_or(Value::Null));
61 acc
62 },
63 )
64 .parse(rest)?;
65
66 if list.is_empty() {
67 Ok((rest, first))
68 } else {
69 list.insert(0, first);
70 let sep = if space {
71 ListSeparator::Slash
72 } else {
73 ListSeparator::SlashNoSpace
74 };
75 Ok((end, Value::List(list, Some(sep), false)))
76 }
77}
78
79pub fn space_list(input: Span) -> PResult<Value> {
80 let (input, first) = se_or_ext_string(input)?;
81 let (input, list) = fold_many0(
82 pair(recognize(ignore_comments), se_or_ext_string),
83 move || vec![first.clone()],
84 |mut list: Vec<Value>, (s, item)| {
85 match (list.last_mut(), s.fragment(), &item) {
86 (
87 Some(Value::Literal(ref mut s1)),
88 b"",
89 Value::Literal(ref s2),
90 ) if s1.is_unquoted() && s2.is_unquoted() => {
91 s1.append(s2);
92 }
93 _ => {
94 list.push(item);
95 }
96 }
97 list
98 },
99 )
100 .parse(input)?;
101 Ok((input, list_or_single(list, ListSeparator::Space)))
102}
103
104pub fn simple_space_list(input: Span) -> PResult<Value> {
105 let (input, first) = single_expression(input)?;
106 let (input, list) = fold_many0(
107 preceded(spacelike2, single_expression),
108 move || vec![first.clone()],
109 |mut list, item| {
110 list.push(item);
111 list
112 },
113 )
114 .parse(input)?;
115 Ok((input, list_or_single(list, ListSeparator::Space)))
116}
117
118fn se_or_ext_string(input: Span) -> PResult<Value> {
119 let result = single_expression(input);
120 if matches!(result, Err(nom::Err::Error(_))) {
121 if let Ok((rest, lit)) = sass_string_ext(input) {
122 return Ok((rest, Value::Literal(lit)));
123 }
124 }
125 result
126}
127
128fn single_expression(input: Span) -> PResult<Value> {
129 let (input1, a) = logic_expression(input)?;
130 fold_many0(
131 (
132 delimited(
133 multispace0,
134 alt((
135 value(Operator::And, tag("and")),
136 value(Operator::Or, tag("or")),
137 )),
138 multispace1,
139 ),
140 single_expression,
141 position,
142 ),
143 move || a.clone(),
144 |a, (op, b, end)| {
145 let pos = input.up_to(&end).to_owned();
146 BinOp::new(a, false, op, false, b, pos).into()
147 },
148 )
149 .parse(input1)
150}
151
152fn logic_expression(input: Span) -> PResult<Value> {
153 let (input1, a) = sum_expression(input)?;
154 fold_many0(
155 (
156 delimited(multispace0, relational_operator, multispace0),
157 sum_expression,
158 position,
159 ),
160 move || a.clone(),
161 |a, (op, b, end)| {
162 let pos = input.up_to(&end).to_owned();
163 BinOp::new(a, true, op, true, b, pos).into()
164 },
165 )
166 .parse(input1)
167}
168
169fn relational_operator(input: Span) -> PResult<Operator> {
170 alt((
171 value(Operator::Equal, tag("==")),
172 value(Operator::NotEqual, tag("!=")),
173 value(Operator::GreaterE, tag(">=")),
174 value(Operator::Greater, tag(">")),
175 value(Operator::LesserE, tag("<=")),
176 value(Operator::Lesser, tag("<")),
177 ))
178 .parse(input)
179}
180
181fn sum_expression(input: Span) -> PResult<Value> {
182 any_additive_expr(term_value, input)
183}
184
185pub fn any_additive_expr<F>(term: F, input: Span) -> PResult<Value>
186where
187 F: Fn(Span) -> PResult<Value>,
188{
189 let (rest, v) = term(input)?;
190 fold_many0(
191 verify(
192 (
193 (
194 ignore_comments,
195 alt((
196 value(Operator::Plus, tag("+")),
197 value(Operator::Minus, tag("-")),
198 )),
199 ignore_comments,
200 ),
201 term,
202 position,
203 ),
204 |((s1, op, s2), t2, _)| {
205 use Value::*;
206 *s2 || !*s1
207 || op == &Operator::Plus
208 || !matches!(&t2, Literal(_) | Numeric(_) | BinOp(_))
209 },
210 ),
211 move || v.clone(),
212 |v, ((s1, op, s2), v2, end)| {
213 let pos = input.up_to(&end).to_owned();
214 BinOp::new(v, s1, op, s2, v2, pos).into()
215 },
216 )
217 .parse(rest)
218}
219
220fn term_value(input: Span) -> PResult<Value> {
221 any_product(single_value, input)
222}
223
224pub fn any_product<F>(factor: F, input: Span) -> PResult<Value>
225where
226 F: Fn(Span) -> PResult<Value>,
227{
228 let (rest, v) = factor(input)?;
229 fold_many0(
230 (
231 ignore_comments,
232 alt((
233 value(Operator::Multiply, tag("*")),
234 value(
235 Operator::Div,
236 terminated(tag("/"), peek(not(tag("/")))),
237 ),
238 value(Operator::Modulo, tag("%")),
239 )),
240 ignore_comments,
241 factor,
242 position,
243 ),
244 move || v.clone(),
245 |v1, (s1, op, s2, v2, end)| {
246 let pos = input.up_to(&end).to_owned();
247 BinOp::new(v1, s1, op, s2, v2, pos).into()
248 },
249 )
250 .parse(rest)
251}
252
253pub fn single_value(input: Span) -> PResult<Value> {
254 match input.first() {
255 Some(b'!') => bang(input),
256 Some(b'&') => value(Value::HereSelector, tag("&")).parse(input),
257 Some(b'"') => map(sass_string_dq, Value::Literal).parse(input),
258 Some(b'\'') => map(sass_string_sq, Value::Literal).parse(input),
259 Some(b'[') => bracket_list(input),
260 Some(b'(') => value_in_parens(input),
261 Some(b'$') => variable_nomod(input),
262 _ => alt((
263 value(Value::True, tag("true")),
264 value(Value::False, tag("false")),
265 unicode_range,
266 into(numeric),
267 variable,
268 hex_color,
269 value(Value::Null, tag("null")),
270 map(special_url, Value::Literal),
271 special_function,
272 function_call_or_string,
273 unary_op,
274 ))
275 .parse(input),
276 }
277}
278
279fn bang(input: Span) -> PResult<Value> {
280 map(
281 map_res(
282 preceded(
283 pair(tag("!"), opt_spacelike),
284 context("Expected \"important\".", tag("important")),
285 ),
286 input_to_string,
287 ),
288 Value::Bang,
289 )
290 .parse(input)
291}
292
293pub fn value_in_parens(input: Span) -> PResult<Value> {
294 let (input0, _p) = preceded(tag("("), opt_spacelike).parse(input)?;
295 if let Ok((input, first_key)) = simple_space_list(input0) {
296 let (input, value) = if let Ok((mut input, first_val)) =
297 preceded(colon, space_list).parse(input)
298 {
299 let mut items = vec![(first_key, first_val)];
300 while let Ok((rest, (key, val))) = pair(
301 preceded(comma, simple_space_list),
302 preceded(colon, space_list),
303 )
304 .parse(input)
305 {
306 items.push((key, val));
307 input = rest;
308 }
309 let (input, _) = opt(comma).parse(input)?;
310 (input, Value::Map(items))
311 } else {
312 (input, Value::Paren(Box::new(first_key), false))
313 };
314 if let Ok((input, _)) = end_paren(input) {
315 return Ok((input, value));
316 }
317 }
318 terminated(
319 alt((
320 map(value_expression, |v| Value::Paren(Box::new(v), false)),
321 map(tag(""), |_| Value::List(vec![], None, false)),
322 )),
323 end_paren,
324 )
325 .parse(input0)
326}
327
328fn comma(input: Span) -> PResult<Span> {
329 delimited(opt_spacelike, tag(","), opt_spacelike).parse(input)
330}
331
332fn colon(input: Span) -> PResult<Span> {
333 delimited(opt_spacelike, tag(":"), opt_spacelike).parse(input)
334}
335
336fn end_paren(input: Span) -> PResult<Span> {
337 preceded(opt_spacelike, tag(")")).parse(input)
338}
339
340fn unicode_range(input: Span) -> PResult<Value> {
341 map(unicode_range_inner, Value::UnicodeRange).parse(input)
342}
343pub(crate) fn unicode_range_inner(input: Span) -> PResult<String> {
344 let (rest, _) = tag_no_case("U+").parse(input)?;
345 let (rest, a) =
346 many_m_n(0, 6, one_of("0123456789ABCDEFabcdef")).parse(rest)?;
347 let (rest, _) = alt((
348 preceded(tag("-"), many_m_n(1, 6, one_of("0123456789ABCDEFabcdef"))),
349 many_m_n(0, 6 - a.len(), one_of("?")),
350 ))
351 .parse(rest)?;
352 let length = rest.location_offset() - input.location_offset();
353 let matched = &input.fragment()[0..length];
354 Ok((rest, from_utf8(matched).unwrap().to_string()))
356}
357
358pub fn bracket_list(input: Span) -> PResult<Value> {
359 let (input, content) =
360 delimited(char('['), opt(value_expression), char(']'))
361 .parse(input)?;
362 Ok((
363 input,
364 match content {
365 Some(Value::List(list, sep, false)) => {
366 Value::List(list, sep, true)
367 }
368 Some(single) => Value::List(vec![single], None, true),
369 None => Value::List(vec![], None, true),
370 },
371 ))
372}
373
374pub fn numeric(input: Span) -> PResult<Numeric> {
375 map(pair(number, unit), |(number, unit)| {
376 Numeric::new(number, unit)
377 })
378 .parse(input)
379}
380
381pub fn number(input: Span) -> PResult<f64> {
382 map_opt(
383 recognize(delimited(
384 opt(one_of("+-")),
385 alt((
386 terminated(digit1, opt(terminated(char('.'), digit1))),
387 preceded(char('.'), digit1),
388 )),
389 opt(delimited(one_of("eE"), opt(one_of("+-")), digit1)),
390 )),
391 |s: Span| from_utf8(s.fragment()).ok()?.parse().ok(),
392 )
393 .parse(input)
394}
395
396pub fn variable_nomod(input: Span) -> PResult<Value> {
397 let (rest, name) = preceded(char('$'), identifier).parse(input)?;
398 let pos = input.up_to(&rest).to_owned();
399 Ok((rest, Value::Variable(name.into(), pos)))
400}
401
402pub fn variable(input: Span) -> PResult<Value> {
403 let (rest, (modules, name)) = pair(
404 many0(terminated(name, tag("."))),
405 preceded(tag("$"), cut(identifier)),
406 )
407 .parse(input)?;
408 let name = if modules.is_empty() {
409 name
410 } else {
411 format!("{}.{}", modules.join("."), name)
412 };
413 let pos = input.up_to(&rest).to_owned();
414 Ok((rest, Value::Variable(name.into(), pos)))
415}
416
417pub fn identifier(input: Span) -> PResult<String> {
418 context("Expected identifier.", name).parse(input)
419}
420
421fn hex_color(input: Span) -> PResult<Value> {
422 let (rest, (r, g, b, a)) = delimited(
423 tag("#"),
424 alt((
425 (hexchar2, hexchar2, hexchar2, opt(hexchar2)),
426 (hexchar1, hexchar1, hexchar1, opt(hexchar1)),
427 )),
428 peek(map(not(alphanumeric1), |_| ())),
429 )
430 .parse(input)?;
431
432 if let Some(a) = a {
433 let rgba = Rgba::from_rgba(r, g, b, a);
434 Ok((rest, Value::Color(rgba, None)))
435 } else {
436 let rgba = Rgba::from_rgb(r, g, b);
437 let length = input.fragment().len() - rest.fragment().len();
438 let raw =
440 from_utf8(&input.fragment()[0..length]).unwrap().to_string();
441 Ok((rest, Value::Color(rgba, Some(raw))))
442 }
443}
444
445pub fn unary_op(input: Span) -> PResult<Value> {
446 map(
447 (
448 alt((
449 value(Operator::Plus, tag("+")),
450 value(Operator::Minus, tag("-")),
451 value(Operator::Div, terminated(tag("/"), spacelike2)),
452 value(Operator::Not, terminated(tag("not"), spacelike2)),
453 )),
454 ignore_comments,
455 single_value,
456 ),
457 |(op, s, v)| match (op, s, v) {
458 (
459 Operator::Minus | Operator::Plus,
460 false,
461 Value::Literal(mut s),
462 ) if s.is_unquoted() => {
463 s.prepend(&op.to_string());
464 Value::Literal(s)
465 }
466 (op, _, v) => Value::UnaryOp(op, Box::new(v)),
467 },
468 )
469 .parse(input)
470}
471
472pub fn special_function(input: Span) -> PResult<Value> {
473 alt((css_function, map(special_function_misc, Value::Literal)))
475 .parse(input)
476}
477
478pub fn function_call_or_string(input: Span) -> PResult<Value> {
479 function_call_or_string_real(input, true)
480}
481pub fn function_call_or_string_rulearg(input: Span) -> PResult<Value> {
482 function_call_or_string_real(input, false)
483}
484fn function_call_or_string_real(
485 input: Span,
486 allow_not: bool,
487) -> PResult<Value> {
488 let (rest, name) = sass_string(input)?;
489
490 if let Some(val) = name.single_raw() {
491 match val {
492 "not" if allow_not => {
493 if let Ok((rest, arg)) =
494 preceded(ignore_comments, single_value).parse(rest)
495 {
496 return Ok((
497 rest,
498 Value::UnaryOp(Operator::Not, Box::new(arg)),
499 ));
500 }
501 }
502 "NaN" => return Ok((rest, Value::scalar(f64::NAN))),
503 "infinity" => return Ok((rest, Value::scalar(f64::INFINITY))),
504 "-infinity" => {
505 return Ok((rest, Value::scalar(f64::NEG_INFINITY)))
506 }
507
508 _ => (),
514 }
515 }
516 if rest.starts_with(b"(") {
517 match call_args(rest) {
518 Ok((rest, args)) => {
519 let pos = input.up_to(&rest).to_owned();
520 return Ok((rest, Value::Call(name, args, pos)));
521 }
522 Err(error) => {
523 if let Ok((rest, lit)) = sass_string_ext(rest) {
524 return Ok((rest, Value::Literal(lit)));
525 } else {
526 return Err(error);
527 }
528 }
529 }
530 }
531 Ok((rest, literal_or_color(name)))
532}
533
534fn literal_or_color(s: SassString) -> Value {
535 if let Some(val) = s.single_raw() {
536 if let Some(rgba) = Rgba::from_name(val) {
537 return Value::Color(rgba, Some(val.to_string()));
538 }
539 }
540 Value::Literal(s)
541}
542
543fn hexchar1(input: Span) -> PResult<u8> {
544 map(hexchar_raw, |one| one * 0x11).parse(input)
545}
546fn hexchar2(input: Span) -> PResult<u8> {
547 map(pair(hexchar_raw, hexchar_raw), |(hi, lo)| hi * 0x10 + lo)
548 .parse(input)
549}
550fn hexchar_raw(input: Span) -> PResult<u8> {
551 map(one_of("0123456789ABCDEFabcdef"), |ch| {
552 ch.to_digit(16).unwrap() as u8
553 })
554 .parse(input)
555}
556
557pub fn dictionary(input: Span) -> PResult<Value> {
558 delimited(
559 preceded(tag("("), opt_spacelike),
560 dictionary_inner,
561 terminated(opt_spacelike, tag(")")),
562 )
563 .parse(input)
564}
565
566pub fn dictionary_inner(input: Span) -> PResult<Value> {
567 let (input, items) = terminated(
568 separated_list1(
569 delimited(opt_spacelike, tag(","), opt_spacelike),
570 pair(
571 sum_expression,
572 preceded(
573 delimited(ignore_comments, tag(":"), opt_spacelike),
574 space_list,
575 ),
576 ),
577 ),
578 opt(delimited(opt_spacelike, tag(","), opt_spacelike)),
579 )
580 .parse(input)?;
581 Ok((input, Value::Map(items.into_iter().collect())))
582}
583
584#[cfg(test)]
585mod test {
586 use super::super::{code_span, parse_value_data};
587 use super::*;
588 use crate::sass::CallArgs;
589 use crate::sass::Value::{Color, List, Literal, Map, Paren};
590 use crate::ScopeRef;
591
592 #[test]
593 fn simple_number() {
594 check_expr("4;", Value::scalar(4.))
595 }
596
597 #[test]
598 fn simple_number_neg() {
599 check_expr("-4;", Value::scalar(-4.))
600 }
601
602 #[test]
603 fn simple_number_pos() {
604 check_expr("+4;", Value::scalar(4.))
605 }
606
607 #[test]
608 fn simple_number_dec() {
609 check_expr("4.34;", Value::scalar(4.34))
610 }
611 #[test]
612 fn simple_number_onlydec() {
613 check_expr(".34;", Value::scalar(0.34))
614 }
615 #[test]
616 fn simple_number_onlydec_neg() {
617 check_expr("-.34;", Value::scalar(-0.34))
618 }
619 #[test]
620 fn simple_number_onlydec_pos() {
621 check_expr("+.34;", Value::scalar(0.34))
622 }
623
624 #[test]
625 fn simple_value_literal() {
626 check_expr("rad;", Literal("rad".into()))
627 }
628
629 #[test]
630 fn simple_value_literal_color() {
631 check_expr(
632 "red;",
633 Color(Rgba::from_rgb(255, 0, 0), Some("red".into())),
634 )
635 }
636
637 #[test]
638 fn simple_value_variable() {
639 match value_expression(code_span(b"$red;").borrow())
640 .map(|(_, value)| value)
641 .unwrap()
642 {
643 Value::Variable(name, _) => assert_eq!(name, "red".into()),
644 val => panic!("Unexpected value {:?}", val),
645 }
646 }
647
648 #[test]
649 fn paren_literal() {
650 check_expr("(rad);", Paren(Box::new(Literal("rad".into())), false))
651 }
652
653 #[test]
654 fn paren_multi() {
655 check_expr(
656 "(rod bloe);",
657 Paren(
658 Box::new(List(
659 vec![Literal("rod".into()), Literal("bloe".into())],
660 Some(ListSeparator::Space),
661 false,
662 )),
663 false,
664 ),
665 )
666 }
667
668 #[test]
669 fn paren_multi_comma() {
670 check_expr(
671 "(rod, bloe);",
672 Paren(
673 Box::new(List(
674 vec![Literal("rod".into()), Literal("bloe".into())],
675 Some(ListSeparator::Comma),
676 false,
677 )),
678 false,
679 ),
680 )
681 }
682
683 #[test]
684 fn multi_comma() {
685 check_expr(
686 "rod, bloe;",
687 List(
688 vec![Literal("rod".into()), Literal("bloe".into())],
689 Some(ListSeparator::Comma),
690 false,
691 ),
692 )
693 }
694
695 #[test]
696 fn paren_multi_comma_trailing() {
697 check_expr(
698 "(rod, bloe, );",
699 Paren(
700 Box::new(List(
701 vec![Literal("rod".into()), Literal("bloe".into())],
702 Some(ListSeparator::Comma),
703 false,
704 )),
705 false,
706 ),
707 )
708 }
709
710 #[test]
711 fn multi_comma_trailing() {
712 check_expr(
713 "rod, bloe, ;",
714 List(
715 vec![Literal("rod".into()), Literal("bloe".into())],
716 Some(ListSeparator::Comma),
717 false,
718 ),
719 )
720 }
721
722 #[test]
723 fn call_no_args() {
724 assert_eq!(
725 parse_call(code_span(b"foo();").borrow()),
726 Ok(("foo".into(), CallArgs::default(), ";".as_bytes())),
727 );
728 }
729
730 #[test]
731 fn call_one_arg() {
732 assert_eq!(
733 parse_call(code_span(b"foo(17);").borrow()),
734 Ok((
735 "foo".into(),
736 CallArgs::new(vec![(None, Value::scalar(17))], false)
737 .unwrap(),
738 ";".as_bytes(),
739 )),
740 );
741 }
742
743 fn parse_call(
745 expr: Span,
746 ) -> Result<(SassString, CallArgs, &[u8]), String> {
747 let (rest, value) =
748 value_expression(expr).map_err(|e| e.to_string())?;
749 if let Value::Call(name, args, _) = value {
750 Ok((name, args, rest.fragment()))
751 } else {
752 Err(format!("Not a call parse result: {:?} {:?}", value, rest))
753 }
754 }
755
756 #[test]
757 fn color_short() {
758 check_expr(
759 "#AbC;",
760 Color(Rgba::from_rgb(0xaa, 0xbb, 0xcc), Some("#AbC".into())),
761 )
762 }
763
764 #[test]
765 fn color_long() {
766 check_expr(
767 "#AaBbCc;",
768 Color(Rgba::from_rgb(0xaa, 0xbb, 0xcc), Some("#AaBbCc".into())),
769 )
770 }
771
772 #[test]
773 fn parse_bracket_array() {
774 check_expr(
775 "[foo bar];",
776 List(
777 vec![Literal("foo".into()), Literal("bar".into())],
778 Some(ListSeparator::Space),
779 true,
780 ),
781 )
782 }
783
784 #[test]
785 fn parse_bracket_comma_array() {
786 check_expr(
787 "[foo, bar];",
788 List(
789 vec![Literal("foo".into()), Literal("bar".into())],
790 Some(ListSeparator::Comma),
791 true,
792 ),
793 )
794 }
795
796 #[test]
797 fn parse_bracket_empty_array() {
798 check_expr("[];", List(vec![], None, true))
799 }
800
801 #[test]
802 fn map_nq() {
803 check_expr(
804 "(foo: bar, baz: 17);",
805 Map(vec![
806 (Literal("foo".into()), Literal("bar".into())),
807 (Literal("baz".into()), Value::scalar(17)),
808 ]
809 .into_iter()
810 .collect()),
811 )
812 }
813
814 fn check_expr(expr: &str, value: Value) {
815 assert_eq!(
816 value_expression(code_span(expr.as_bytes()).borrow())
817 .map(|(rest, value)| (rest.fragment(), value))
818 .unwrap(),
819 (&b";"[..], value),
820 )
821 }
822
823 #[test]
824 fn parse_extended_literal() -> Result<(), crate::Error> {
825 assert_eq!(
826 parse_value_data(b"http://#{\")\"}.com/")?
827 .evaluate(ScopeRef::new_global(Default::default()))?
828 .format(Default::default())
829 .to_string(),
830 "http://).com/".to_string(),
831 );
832 Ok(())
833 }
834 #[test]
835 fn parse_extended_literal_in_arg() -> Result<(), crate::Error> {
836 assert_eq!(
837 parse_value_data(b"url(http://#{\")\"}.com/)")?
838 .evaluate(ScopeRef::new_global(Default::default()))?
839 .format(Default::default())
840 .to_string(),
841 "url(http://).com/)".to_string(),
842 );
843 Ok(())
844 }
845 #[test]
846 fn parse_extended_literal_in_arg_2() -> Result<(), crate::Error> {
847 assert_eq!(
848 parse_value_data(b"url(//#{\")\"}.com/)")?
849 .evaluate(ScopeRef::new_global(Default::default()))?
850 .format(Default::default())
851 .to_string(),
852 "url(//).com/)".to_string(),
853 );
854 Ok(())
855 }
856}