rune/ast/
lit_byte_str.rs

1use crate::alloc::borrow::Cow;
2use crate::ast::prelude::*;
3
4#[test]
5#[cfg(not(miri))]
6fn ast_parse() {
7    rt::<ast::LitByteStr>("b\"hello world\"");
8    rt::<ast::LitByteStr>("b\"hello\\nworld\"");
9}
10
11/// A string literal.
12///
13/// * `"Hello World"`.
14#[derive(Debug, TryClone, Clone, Copy, PartialEq, Eq, Spanned)]
15#[try_clone(copy)]
16#[non_exhaustive]
17pub struct LitByteStr {
18    /// The span corresponding to the literal.
19    pub span: Span,
20    /// If the string literal is escaped.
21    #[rune(skip)]
22    pub source: ast::StrSource,
23}
24
25impl LitByteStr {
26    fn parse_escaped(&self, span: Span, source: &str) -> Result<Vec<u8>> {
27        let mut buffer = Vec::try_with_capacity(source.len())?;
28
29        let start = span.start.into_usize();
30
31        let mut it = source
32            .char_indices()
33            .map(|(n, c)| (start + n, c))
34            .peekable();
35
36        while let Some((start, c)) = it.next() {
37            buffer.try_extend(match c {
38                '\\' => {
39                    match ast::unescape::parse_byte_escape(
40                        &mut it,
41                        ast::unescape::WithLineCont(true),
42                    ) {
43                        Ok(c) => c,
44                        Err(kind) => {
45                            let end = it
46                                .next()
47                                .map(|n| n.0)
48                                .unwrap_or_else(|| span.end.into_usize());
49                            return Err(compile::Error::new(Span::new(start, end), kind));
50                        }
51                    }
52                }
53                c => Some(c as u8),
54            })?;
55        }
56
57        Ok(buffer)
58    }
59}
60
61impl ToAst for LitByteStr {
62    fn to_ast(span: Span, kind: ast::Kind) -> compile::Result<Self> {
63        match kind {
64            K![bytestr(source)] => Ok(Self { span, source }),
65            _ => Err(compile::Error::expected(
66                ast::Token { span, kind },
67                Self::into_expectation(),
68            )),
69        }
70    }
71
72    #[inline]
73    fn matches(kind: &ast::Kind) -> bool {
74        matches!(kind, K![bytestr])
75    }
76
77    #[inline]
78    fn into_expectation() -> Expectation {
79        Expectation::Description("byte string")
80    }
81}
82
83impl Parse for LitByteStr {
84    fn parse(parser: &mut Parser<'_>) -> Result<Self> {
85        let t = parser.next()?;
86        Self::to_ast(t.span, t.kind)
87    }
88}
89
90impl<'a> Resolve<'a> for LitByteStr {
91    type Output = Cow<'a, [u8]>;
92
93    fn resolve(&self, cx: ResolveContext<'a>) -> Result<Cow<'a, [u8]>> {
94        let span = self.span;
95
96        let text = match self.source {
97            ast::StrSource::Text(text) => text,
98            ast::StrSource::Synthetic(id) => {
99                let bytes = cx.storage.get_byte_string(id).ok_or_else(|| {
100                    compile::Error::new(
101                        span,
102                        ErrorKind::BadSyntheticId {
103                            kind: SyntheticKind::ByteString,
104                            id,
105                        },
106                    )
107                })?;
108
109                return Ok(Cow::Borrowed(bytes));
110            }
111        };
112
113        let span = span.trim_start(2u32).trim_end(1u32);
114        let string = cx
115            .sources
116            .source(text.source_id, span)
117            .ok_or_else(|| compile::Error::new(span, ErrorKind::BadSlice))?;
118
119        Ok(if text.escaped {
120            Cow::Owned(self.parse_escaped(span, string)?)
121        } else {
122            Cow::Borrowed(string.as_bytes())
123        })
124    }
125}
126
127impl ToTokens for LitByteStr {
128    fn to_tokens(
129        &self,
130        _: &mut MacroContext<'_, '_, '_>,
131        stream: &mut TokenStream,
132    ) -> alloc::Result<()> {
133        stream.push(ast::Token {
134            span: self.span,
135            kind: ast::Kind::ByteStr(self.source),
136        })
137    }
138}