rune/ast/
lit_byte.rs

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