1
//! Double-quoted and triple-quoted string literals.
2

            
3
use winnow::combinator::{alt, cut_err};
4
use winnow::error::{AddContext, ContextError, ErrMode, ModalResult, StrContext, StrContextValue};
5
use winnow::prelude::*;
6
use winnow::token::{any, take_till};
7

            
8
use crate::ast::Expr;
9

            
10
5772905
pub(super) fn parse_string(input: &mut &str) -> ModalResult<Expr> {
11
5772905
    alt((parse_triple_quoted_string, parse_double_quoted_string)).parse_next(input)
12
5772905
}
13

            
14
5772293
fn parse_double_quoted_string(input: &mut &str) -> ModalResult<Expr> {
15
5772293
    let _ = '"'.parse_next(input)?;
16
49995
    let mut result = String::new();
17

            
18
    loop {
19
837064
        let chunk: &str = take_till(0.., |c| c == '"' || c == '\\').parse_next(input)?;
20
62984
        result.push_str(chunk);
21

            
22
62984
        match cut_err(any)
23
62984
            .context(StrContext::Label("string"))
24
62984
            .context(StrContext::Expected(StrContextValue::Description(
25
62984
                "closing quote",
26
62984
            )))
27
62984
            .parse_next(input)?
28
        {
29
49927
            '"' => return Ok(Expr::String(result)),
30
            '\\' => {
31
12989
                let escaped = any.parse_next(input)?;
32
12989
                match escaped {
33
477
                    'n' => result.push('\n'),
34
476
                    't' => result.push('\t'),
35
68
                    'r' => result.push('\r'),
36
408
                    '\\' => result.push('\\'),
37
11560
                    '"' => result.push('"'),
38
                    c => {
39
                        result.push('\\');
40
                        result.push(c);
41
                    }
42
                }
43
            }
44
            _ => unreachable!(),
45
        }
46
    }
47
5772293
}
48

            
49
5772905
fn parse_triple_quoted_string(input: &mut &str) -> ModalResult<Expr> {
50
5772905
    let _ = "\"\"\"".parse_next(input)?;
51
612
    let mut content = String::new();
52

            
53
    loop {
54
39032
        if input.starts_with("\"\"\"") {
55
612
            let _ = "\"\"\"".parse_next(input)?;
56
612
            return Ok(Expr::String(content));
57
38420
        }
58
38420
        if input.is_empty() {
59
            return Err(ErrMode::Cut(
60
                ContextError::new()
61
                    .add_context(input, &input.checkpoint(), StrContext::Label("string"))
62
                    .add_context(
63
                        input,
64
                        &input.checkpoint(),
65
                        StrContext::Expected(StrContextValue::Description("closing \"\"\"")),
66
                    ),
67
            ));
68
38420
        }
69
38420
        content.push(any.parse_next(input)?);
70
    }
71
5772905
}