rune/ast/
lit_char.rs

1use crate::ast::prelude::*;
2
3#[test]
4#[cfg(not(miri))]
5fn ast_parse() {
6    rt::<ast::LitChar>("'a'");
7    rt::<ast::LitChar>("'\\0'");
8    rt::<ast::LitChar>("'\\n'");
9    rt::<ast::LitChar>("'\\r'");
10    rt::<ast::LitChar>("'\\''");
11}
12
13/// A character literal.
14#[derive(Debug, TryClone, Clone, Copy, PartialEq, Eq, Spanned)]
15#[try_clone(copy)]
16#[non_exhaustive]
17pub struct LitChar {
18    /// The span corresponding to the literal.
19    pub span: Span,
20    /// The source of the literal character.
21    #[rune(skip)]
22    pub source: ast::CopySource<char>,
23}
24
25impl ToAst for LitChar {
26    fn to_ast(span: Span, kind: ast::Kind) -> compile::Result<Self> {
27        match kind {
28            K![char(source)] => Ok(LitChar { 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![char])
39    }
40
41    #[inline]
42    fn into_expectation() -> Expectation {
43        Expectation::Description("char")
44    }
45}
46
47impl Parse for LitChar {
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 LitChar {
55    type Output = char;
56
57    fn resolve(&self, cx: ResolveContext<'a>) -> Result<char> {
58        let source_id = match self.source {
59            ast::CopySource::Inline(c) => return Ok(c),
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.narrow(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::BadCharLiteral));
81            }
82        };
83
84        let c = match c {
85            '\\' => {
86                let c = match ast::unescape::parse_char_escape(
87                    &mut it,
88                    ast::unescape::WithTemplate(false),
89                    ast::unescape::WithLineCont(false),
90                ) {
91                    Ok(c) => c,
92                    Err(kind) => {
93                        let end = it
94                            .next()
95                            .map(|n| n.0)
96                            .unwrap_or_else(|| span.end.into_usize());
97                        return Err(compile::Error::new(Span::new(start, end), kind));
98                    }
99                };
100
101                match c {
102                    Some(c) => c,
103                    None => {
104                        let end = it
105                            .next()
106                            .map(|n| n.0)
107                            .unwrap_or_else(|| span.end.into_usize());
108                        return Err(compile::Error::new(
109                            Span::new(start, end),
110                            ErrorKind::BadCharLiteral,
111                        ));
112                    }
113                }
114            }
115            c => c,
116        };
117
118        // Too many characters in literal.
119        if it.next().is_some() {
120            return Err(compile::Error::new(span, ErrorKind::BadCharLiteral));
121        }
122
123        Ok(c)
124    }
125}
126
127impl ToTokens for LitChar {
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::Char(self.source),
136        })
137    }
138}