1use crate::ast::prelude::*;
2use crate::compile::{num, WithSpan};
3
4use ast::token::NumberSize;
5
6#[test]
7#[cfg(not(miri))]
8fn ast_parse() {
9 rt::<ast::LitNumber>("42");
10 rt::<ast::LitNumber>("42.42");
11 rt::<ast::LitNumber>("0.42");
12 rt::<ast::LitNumber>("0.42e10");
13}
14
15#[derive(Debug, TryClone, Clone, Copy, PartialEq, Eq, Spanned)]
20#[try_clone(copy)]
21#[non_exhaustive]
22pub struct LitNumber {
23 pub span: Span,
25 #[rune(skip)]
27 pub source: ast::NumberSource,
28}
29
30impl ToAst for LitNumber {
31 fn to_ast(span: Span, kind: ast::Kind) -> compile::Result<Self> {
32 match kind {
33 K![number(source)] => Ok(LitNumber { source, span }),
34 _ => Err(compile::Error::expected(
35 ast::Token { span, kind },
36 Self::into_expectation(),
37 )),
38 }
39 }
40
41 #[inline]
42 fn matches(kind: &ast::Kind) -> bool {
43 matches!(kind, K![number])
44 }
45
46 #[inline]
47 fn into_expectation() -> Expectation {
48 Expectation::Description("number")
49 }
50}
51
52impl Parse for LitNumber {
53 fn parse(parser: &mut Parser<'_>) -> Result<Self> {
54 let t = parser.next()?;
55 Self::to_ast(t.span, t.kind)
56 }
57}
58
59impl<'a> Resolve<'a> for LitNumber {
60 type Output = ast::Number;
61
62 fn resolve(&self, cx: ResolveContext<'a, '_>) -> Result<ast::Number> {
63 let span = self.span;
64
65 let text = match self.source {
66 ast::NumberSource::Synthetic(id) => {
67 let Some(number) = cx.storage.get_number(id) else {
68 return Err(compile::Error::new(
69 span,
70 ErrorKind::BadSyntheticId {
71 kind: SyntheticKind::Number,
72 id,
73 },
74 ));
75 };
76
77 return Ok((*number).try_clone()?);
78 }
79 ast::NumberSource::Text(text) => text,
80 };
81
82 let string = cx
83 .sources
84 .source(text.source_id, text.number)
85 .ok_or_else(|| compile::Error::new(span, ErrorKind::BadSlice))?;
86
87 let suffix = cx
88 .sources
89 .source(text.source_id, text.suffix)
90 .ok_or_else(|| compile::Error::new(span, ErrorKind::BadSlice))?;
91
92 let suffix = match suffix {
93 "u8" => Some(ast::NumberSuffix::Unsigned(text.suffix, NumberSize::S8)),
94 "u16" => Some(ast::NumberSuffix::Unsigned(text.suffix, NumberSize::S16)),
95 "u32" => Some(ast::NumberSuffix::Unsigned(text.suffix, NumberSize::S32)),
96 "u64" => Some(ast::NumberSuffix::Unsigned(text.suffix, NumberSize::S64)),
97 "i8" => Some(ast::NumberSuffix::Signed(text.suffix, NumberSize::S8)),
98 "i16" => Some(ast::NumberSuffix::Signed(text.suffix, NumberSize::S16)),
99 "i32" => Some(ast::NumberSuffix::Signed(text.suffix, NumberSize::S32)),
100 "i64" => Some(ast::NumberSuffix::Signed(text.suffix, NumberSize::S64)),
101 "f32" | "f64" => Some(ast::NumberSuffix::Float(text.suffix)),
102 "" => None,
103 _ => {
104 return Err(compile::Error::new(
105 text.suffix,
106 ErrorKind::UnsupportedSuffix,
107 ))
108 }
109 };
110
111 if matches!(
112 (suffix, text.is_fractional),
113 (Some(ast::NumberSuffix::Float(..)), _) | (None, true)
114 ) {
115 let number: f64 = num::from_float(cx.scratch, string).with_span(span)?;
116
117 return Ok(ast::Number {
118 value: ast::NumberValue::Float(number),
119 suffix,
120 });
121 }
122
123 let parser = match text.base {
124 ast::NumberBase::Binary => num::from_ascii_binary,
125 ast::NumberBase::Octal => num::from_ascii_octal,
126 ast::NumberBase::Hex => num::from_ascii_hex,
127 ast::NumberBase::Decimal => num::from_ascii_decimal,
128 };
129
130 let number = parser(string.as_bytes())
131 .ok_or_else(|| ErrorKind::BadNumberLiteral)
132 .with_span(span)?;
133
134 Ok(ast::Number {
135 value: ast::NumberValue::Integer(number as i128),
136 suffix,
137 })
138 }
139}
140
141impl ToTokens for LitNumber {
142 fn to_tokens(
143 &self,
144 _: &mut MacroContext<'_, '_, '_>,
145 stream: &mut TokenStream,
146 ) -> alloc::Result<()> {
147 stream.push(ast::Token {
148 span: self.span,
149 kind: ast::Kind::Number(self.source),
150 })
151 }
152}