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#[derive(Debug, TryClone, Clone, Copy, PartialEq, Eq, Spanned)]
15#[try_clone(copy)]
16#[non_exhaustive]
17pub struct LitChar {
18 pub span: Span,
20 #[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 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}