1use alloy_primitives::{Address, U256};
2use solar_interface::{ByteSymbol, Span, Symbol, diagnostics::ErrorGuaranteed, kw};
3use std::fmt;
4
5#[derive(Clone, Copy, Debug)]
13pub struct Lit<'ast> {
14 pub span: Span,
16 pub symbol: Symbol,
22 pub kind: LitKind<'ast>,
25}
26
27impl fmt::Display for Lit<'_> {
28 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29 let Self { ref kind, symbol, span: _ } = *self;
30 match kind {
31 LitKind::Str(s, ..) => write!(f, "{}\"{symbol}\"", s.prefix()),
32 LitKind::Number(_)
33 | LitKind::Rational(_)
34 | LitKind::Err(_)
35 | LitKind::Address(_)
36 | LitKind::Bool(_) => write!(f, "{symbol}"),
37 }
38 }
39}
40
41impl Lit<'_> {
42 pub fn first_span(&self) -> Span {
44 if let LitKind::Str(kind, _, extra) = &self.kind
45 && !extra.is_empty()
46 {
47 let str_len = kind.prefix().len() + 1 + self.symbol.as_str().len() + 1;
48 return self.span.with_hi(self.span.lo() + str_len as u32);
49 }
50 self.span
51 }
52
53 pub fn literals(&self) -> impl Iterator<Item = (Span, Symbol)> + '_ {
55 let extra = if let LitKind::Str(_, _, extra) = self.kind { extra } else { &[] };
56 std::iter::once((self.first_span(), self.symbol)).chain(extra.iter().copied())
57 }
58
59 pub fn copy_without_data<'a>(&self) -> Lit<'a> {
61 if let LitKind::Str(str_kind, byte_symbol, items) = self.kind
62 && !items.is_empty()
63 {
64 return Lit {
65 span: self.span,
66 symbol: self.symbol,
67 kind: LitKind::Str(str_kind, byte_symbol, &[]),
68 };
69 }
70
71 unsafe { std::mem::transmute::<Lit<'_>, Lit<'a>>(*self) }
73 }
74}
75
76#[derive(Clone, Copy)]
78pub enum LitKind<'ast> {
79 Str(StrKind, ByteSymbol, &'ast [(Span, Symbol)]),
99 Number(U256),
101 Rational(num_rational::Ratio<U256>),
106 Address(Address),
108 Bool(bool),
110 Err(ErrorGuaranteed),
112}
113
114impl fmt::Debug for LitKind<'_> {
115 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116 match self {
117 Self::Str(kind, value, extra) => {
118 write!(f, "{kind:?}(")?;
119 let value = value.as_byte_str();
120 if let Ok(utf8) = std::str::from_utf8(value) {
121 write!(f, "{utf8:?}")?;
122 } else {
123 f.write_str(&alloy_primitives::hex::encode_prefixed(value))?;
124 }
125 if !extra.is_empty() {
126 write!(f, ", {extra:?}")?;
127 }
128 f.write_str(")")
129 }
130 Self::Number(value) => write!(f, "Number({value:?})"),
131 Self::Rational(value) => write!(f, "Rational({value:?})"),
132 Self::Address(value) => write!(f, "Address({value:?})"),
133 Self::Bool(value) => write!(f, "Bool({value:?})"),
134 Self::Err(_) => write!(f, "Err"),
135 }
136 }
137}
138
139impl LitKind<'_> {
140 pub fn description(&self) -> &'static str {
142 match self {
143 Self::Str(kind, ..) => kind.description(),
144 Self::Number(_) => "number",
145 Self::Rational(_) => "rational",
146 Self::Address(_) => "address",
147 Self::Bool(_) => "boolean",
148 Self::Err(_) => "<error>",
149 }
150 }
151}
152
153#[derive(Clone, Debug)]
155pub struct StrLit {
156 pub span: Span,
158 pub value: Symbol,
160}
161
162#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
164pub enum StrKind {
165 Str,
167 Unicode,
169 Hex,
171}
172
173impl StrKind {
174 pub fn description(self) -> &'static str {
176 match self {
177 Self::Str => "string",
178 Self::Unicode => "unicode string",
179 Self::Hex => "hex string",
180 }
181 }
182
183 #[doc(alias = "to_str")]
185 pub fn prefix(self) -> &'static str {
186 match self {
187 Self::Str => "",
188 Self::Unicode => "unicode",
189 Self::Hex => "hex",
190 }
191 }
192
193 #[doc(alias = "to_symbol")]
195 pub fn prefix_symbol(self) -> Symbol {
196 match self {
197 Self::Str => kw::Empty,
198 Self::Unicode => kw::Unicode,
199 Self::Hex => kw::Hex,
200 }
201 }
202}
203
204#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
206pub enum SubDenomination {
207 Ether(EtherSubDenomination),
209 Time(TimeSubDenomination),
211}
212
213impl fmt::Display for SubDenomination {
214 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215 match self {
216 Self::Ether(sub_denomination) => sub_denomination.fmt(f),
217 Self::Time(sub_denomination) => sub_denomination.fmt(f),
218 }
219 }
220}
221
222impl SubDenomination {
223 pub const fn to_str(self) -> &'static str {
225 match self {
226 Self::Ether(sub_denomination) => sub_denomination.to_str(),
227 Self::Time(sub_denomination) => sub_denomination.to_str(),
228 }
229 }
230
231 pub const fn to_symbol(self) -> Symbol {
233 match self {
234 Self::Ether(sub_denomination) => sub_denomination.to_symbol(),
235 Self::Time(sub_denomination) => sub_denomination.to_symbol(),
236 }
237 }
238
239 pub const fn value(self) -> u64 {
241 match self {
242 Self::Ether(sub_denomination) => sub_denomination.wei(),
243 Self::Time(sub_denomination) => sub_denomination.seconds(),
244 }
245 }
246}
247
248#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
250pub enum EtherSubDenomination {
251 Wei,
253 Gwei,
255 Ether,
257}
258
259impl fmt::Display for EtherSubDenomination {
260 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
261 f.write_str(self.to_str())
262 }
263}
264
265impl EtherSubDenomination {
266 pub const fn to_str(self) -> &'static str {
268 match self {
269 Self::Wei => "wei",
270 Self::Gwei => "gwei",
271 Self::Ether => "ether",
272 }
273 }
274
275 pub const fn to_symbol(self) -> Symbol {
277 match self {
278 Self::Wei => kw::Wei,
279 Self::Gwei => kw::Gwei,
280 Self::Ether => kw::Ether,
281 }
282 }
283
284 pub const fn wei(self) -> u64 {
286 match self {
288 Self::Wei => 1,
289 Self::Gwei => 1_000_000_000,
290 Self::Ether => 1_000_000_000_000_000_000,
291 }
292 }
293}
294
295#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
297pub enum TimeSubDenomination {
298 Seconds,
300 Minutes,
302 Hours,
304 Days,
306 Weeks,
308 Years,
310}
311
312impl fmt::Display for TimeSubDenomination {
313 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
314 f.write_str(self.to_str())
315 }
316}
317
318impl TimeSubDenomination {
319 pub const fn to_str(self) -> &'static str {
321 match self {
322 Self::Seconds => "seconds",
323 Self::Minutes => "minutes",
324 Self::Hours => "hours",
325 Self::Days => "days",
326 Self::Weeks => "weeks",
327 Self::Years => "years",
328 }
329 }
330
331 pub const fn to_symbol(self) -> Symbol {
333 match self {
334 Self::Seconds => kw::Seconds,
335 Self::Minutes => kw::Minutes,
336 Self::Hours => kw::Hours,
337 Self::Days => kw::Days,
338 Self::Weeks => kw::Weeks,
339 Self::Years => kw::Years,
340 }
341 }
342
343 pub const fn seconds(self) -> u64 {
345 match self {
347 Self::Seconds => 1,
348 Self::Minutes => 60,
349 Self::Hours => 3_600,
350 Self::Days => 86_400,
351 Self::Weeks => 604_800,
352 Self::Years => 31_536_000,
353 }
354 }
355}
356
357#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
359pub enum Base {
360 Binary = 2,
362 Octal = 8,
364 Decimal = 10,
366 Hexadecimal = 16,
368}
369
370impl fmt::Display for Base {
371 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
372 self.to_str().fmt(f)
373 }
374}
375
376impl Base {
377 pub fn to_str(self) -> &'static str {
379 match self {
380 Self::Binary => "binary",
381 Self::Octal => "octal",
382 Self::Decimal => "decimal",
383 Self::Hexadecimal => "hexadecimal",
384 }
385 }
386}
387
388#[cfg(test)]
389mod tests {
390 use super::*;
391 use solar_interface::{BytePos, enter};
392
393 fn bs(s: &[u8]) -> ByteSymbol {
394 ByteSymbol::intern(s)
395 }
396
397 #[test]
398 fn literal_fmt() {
399 enter(|| {
400 let lit = LitKind::Str(StrKind::Str, bs(b"hello world"), &[]);
401 assert_eq!(lit.description(), "string");
402 assert_eq!(format!("{lit:?}"), "Str(\"hello world\")");
403
404 let lit = LitKind::Str(StrKind::Str, bs(b"hello\0world"), &[]);
405 assert_eq!(lit.description(), "string");
406 assert_eq!(format!("{lit:?}"), "Str(\"hello\\0world\")");
407
408 let lit = LitKind::Str(StrKind::Str, bs(&[255u8][..]), &[]);
409 assert_eq!(lit.description(), "string");
410 assert_eq!(format!("{lit:?}"), "Str(0xff)");
411
412 let extra = [(Span::new(BytePos(69), BytePos(420)), Symbol::intern("world"))];
413 let lit = LitKind::Str(StrKind::Str, bs(b"hello world"), &extra);
414 assert_eq!(lit.description(), "string");
415 assert_eq!(format!("{lit:?}"), "Str(\"hello world\", [(Span(69..420), \"world\")])");
416 })
417 }
418}