rune/ast/
lit_number.rs
1use crate::ast::prelude::*;
2
3use ast::token::NumberSize;
4use num::Num;
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 fn err_span<E>(span: Span) -> impl Fn(E) -> compile::Error {
64 move |_| compile::Error::new(span, ErrorKind::BadNumberLiteral)
65 }
66
67 let span = self.span;
68
69 let text = match self.source {
70 ast::NumberSource::Synthetic(id) => {
71 let Some(number) = cx.storage.get_number(id) else {
72 return Err(compile::Error::new(
73 span,
74 ErrorKind::BadSyntheticId {
75 kind: SyntheticKind::Number,
76 id,
77 },
78 ));
79 };
80
81 return Ok((*number).try_clone()?);
82 }
83 ast::NumberSource::Text(text) => text,
84 };
85
86 let string = cx
87 .sources
88 .source(text.source_id, text.number)
89 .ok_or_else(|| compile::Error::new(span, ErrorKind::BadSlice))?;
90
91 let suffix = cx
92 .sources
93 .source(text.source_id, text.suffix)
94 .ok_or_else(|| compile::Error::new(span, ErrorKind::BadSlice))?;
95
96 let suffix = match suffix {
97 "u8" => Some(ast::NumberSuffix::Unsigned(text.suffix, NumberSize::S8)),
98 "u16" => Some(ast::NumberSuffix::Unsigned(text.suffix, NumberSize::S16)),
99 "u32" => Some(ast::NumberSuffix::Unsigned(text.suffix, NumberSize::S32)),
100 "u64" => Some(ast::NumberSuffix::Unsigned(text.suffix, NumberSize::S64)),
101 "i8" => Some(ast::NumberSuffix::Signed(text.suffix, NumberSize::S8)),
102 "i16" => Some(ast::NumberSuffix::Signed(text.suffix, NumberSize::S16)),
103 "i32" => Some(ast::NumberSuffix::Signed(text.suffix, NumberSize::S32)),
104 "i64" => Some(ast::NumberSuffix::Signed(text.suffix, NumberSize::S64)),
105 "f32" | "f64" => Some(ast::NumberSuffix::Float(text.suffix)),
106 "" => None,
107 _ => {
108 return Err(compile::Error::new(
109 text.suffix,
110 ErrorKind::UnsupportedSuffix,
111 ))
112 }
113 };
114
115 if matches!(
116 (suffix, text.is_fractional),
117 (Some(ast::NumberSuffix::Float(..)), _) | (None, true)
118 ) {
119 let number: f64 = string
120 .trim_matches(|c: char| c == '_')
121 .parse()
122 .map_err(err_span(span))?;
123
124 return Ok(ast::Number {
125 value: ast::NumberValue::Float(number),
126 suffix,
127 });
128 }
129
130 let radix = match text.base {
131 ast::NumberBase::Binary => 2,
132 ast::NumberBase::Octal => 8,
133 ast::NumberBase::Hex => 16,
134 ast::NumberBase::Decimal => 10,
135 };
136
137 let number = num::BigInt::from_str_radix(string, radix).map_err(err_span(span))?;
138
139 Ok(ast::Number {
140 value: ast::NumberValue::Integer(number),
141 suffix,
142 })
143 }
144}
145
146impl ToTokens for LitNumber {
147 fn to_tokens(
148 &self,
149 _: &mut MacroContext<'_, '_, '_>,
150 stream: &mut TokenStream,
151 ) -> alloc::Result<()> {
152 stream.push(ast::Token {
153 span: self.span,
154 kind: ast::Kind::Number(self.source),
155 })
156 }
157}