rsass/parser/
value.rs

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    // The unwrap should be ok, as only ascii is matched.
355    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        // Unwrap should be ok as only ascii is matched.
439        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    // Either a nice semantic css function or a fallback with interpolation.
474    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            /* TODO: true, false and null should end up here, but can't as long as '.' is a normal part of a string.
509            "true" => return Ok((rest, Value::True)),
510            "false" => return Ok((rest, Value::False)),
511            "null" => return Ok((rest, Value::Null)),
512             */
513            _ => (),
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    // test helper
744    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}