1use syn::{self, BinOp as B, Expr as E, Lit as L, UnOp as U};
10
11fn parse_lit_int(mut s: &str) -> Option<u128> {
14 pub fn byte<S: AsRef<[u8]> + ?Sized>(s: &S, idx: usize) -> u8 {
17 let s = s.as_ref();
18 if idx < s.len() {
19 s[idx]
20 } else {
21 0
22 }
23 }
24
25 let base = match (byte(s, 0), byte(s, 1)) {
26 (b'0', b'x') => {
27 s = &s[2..];
28 16
29 }
30 (b'0', b'o') => {
31 s = &s[2..];
32 8
33 }
34 (b'0', b'b') => {
35 s = &s[2..];
36 2
37 }
38 (b'0'..=b'9', _) => 10,
39 _ => unreachable!(),
40 };
41
42 let mut value = 0u128;
43 loop {
44 let b = byte(s, 0);
45 let digit = match b {
46 b'0'..=b'9' => u128::from(b - b'0'),
47 b'a'..=b'f' if base > 10 => 10 + u128::from(b - b'a'),
48 b'A'..=b'F' if base > 10 => 10 + u128::from(b - b'A'),
49 b'_' => {
50 s = &s[1..];
51 continue;
52 }
53 b'.' if base == 10 => return None,
56 b'e' | b'E' if base == 10 => return None,
57 _ => break,
58 };
59
60 if digit >= base {
61 panic!("Unexpected digit {:x} out of base range", digit);
62 }
63
64 value = value.checked_mul(base)?.checked_add(digit)?;
65 s = &s[1..];
66 }
67
68 Some(value)
69}
70
71fn parse_suffix(lit: &str) -> Option<&'static str> {
73 [
74 "i8", "i16", "i32", "i64", "i128", "isize", "u8", "u16", "u32", "u64",
75 "u128", "usize",
76 ]
77 .iter()
78 .find(|s| lit.ends_with(*s))
79 .copied()
80}
81
82fn eval_str_int(lit: &str) -> Option<u128> {
84 let val = parse_lit_int(lit)?;
85 let checked_val = if let Some(suffix) = parse_suffix(lit) {
86 match suffix {
87 "i8" if val <= i8::MAX as u128 => val,
88 "i16" if val <= i16::MAX as u128 => val,
89 "i32" if val <= i32::MAX as u128 => val,
90 "i64" if val <= i64::MAX as u128 => val,
91 "u8" if val <= u128::from(u8::MAX) => val,
92 "u16" if val <= u128::from(u16::MAX) => val,
93 "u32" if val <= u128::from(u32::MAX) => val,
94 "u64" if val <= u128::from(u64::MAX) => val,
95 "usize" if val <= usize::MAX as u128 => val,
96 "isize" if val <= isize::MAX as u128 => val,
97 "u128" => val,
98 "i128" if val <= i128::MAX as u128 => val,
99
100 _ => return None,
102 }
103 } else {
104 val
105 };
106
107 Some(checked_val)
108}
109
110fn eval_lit_int(lit: &syn::LitInt) -> Option<u128> {
112 let lit = lit.to_string();
113 eval_str_int(&lit)
114}
115
116fn eval_lit_verbatim(lit: &proc_macro2::Literal) -> Option<u128> {
118 let lit = lit.to_string();
119 eval_str_int(&lit)
120}
121
122fn eval_lit(lit: &syn::ExprLit) -> Option<u128> {
124 match &lit.lit {
125 L::Int(lit) => eval_lit_int(lit),
126 L::Byte(lit) => Some(u128::from(lit.value())),
127 L::Verbatim(lit) => eval_lit_verbatim(lit),
128 _ => None,
129 }
130}
131
132fn eval_binary(bin: &syn::ExprBinary) -> Option<u128> {
134 use std::u32;
135
136 let l = eval_expr(&bin.left)?;
137 let r = eval_expr(&bin.right)?;
138 Some(match bin.op {
139 B::Add(_) => l.checked_add(r)?,
140 B::Sub(_) => l.checked_sub(r)?,
141 B::Mul(_) => l.checked_mul(r)?,
142 B::Div(_) => l.checked_div(r)?,
143 B::Rem(_) => l.checked_rem(r)?,
144 B::BitXor(_) => l ^ r,
145 B::BitAnd(_) => l & r,
146 B::BitOr(_) => l | r,
147 B::Shl(_) if r <= u128::from(u32::MAX) => l.checked_shl(r as u32)?,
148 B::Shr(_) if r <= u128::from(u32::MAX) => l.checked_shr(r as u32)?,
149 _ => return None,
150 })
151}
152
153fn eval_unary(expr: &syn::ExprUnary) -> Option<u128> {
155 if let U::Not(_) = expr.op {
156 Some(!eval_expr(&expr.expr)?)
157 } else {
158 None
159 }
160}
161
162pub fn eval_expr(expr: &E) -> Option<u128> {
164 match expr {
165 E::Lit(expr) => eval_lit(expr),
166 E::Binary(expr) => eval_binary(expr),
167 E::Unary(expr) => eval_unary(expr),
168 E::Paren(expr) => eval_expr(&expr.expr),
169 E::Group(expr) => eval_expr(&expr.expr),
170 _ => None,
171 }
172}
173
174#[cfg(test)]
175mod test {
176 use super::*;
177
178 fn eval(expr: &str) -> Option<u128> {
179 use syn::parse_str;
180 eval_expr(&parse_str(expr).expect("not a valid expression"))
181 }
182
183 macro_rules! test {
184 ($($name: ident, $case: expr => $result:expr;)*) => {$(
185 #[test] fn $name() { assert_eq!(eval($case), $result); }
186 )*};
187 }
188
189 test! {
190 accept_lit_bare, "1" => Some(1);
191 accept_lit_bare_max, "340282366920938463463374607431768211455"
192 => Some(340282366920938463463374607431768211455);
193 reject_lit_bare_overflow, "340282366920938463463374607431768211456" => None;
194 accept_lit_u8_max, "255u8" => Some(255);
195 accept_lit_u16_max, "65535u16" => Some(65535);
196 accept_lit_u32_max, "4294967295u32" => Some(4294967295);
197 accept_lit_u64_max, "18446744073709551615u64" => Some(18446744073709551615);
198 accept_lit_u128_max, "340282366920938463463374607431768211455u128"
199 => Some(340282366920938463463374607431768211455);
200 reject_lit_u8_overflow, "256u8" => None;
201 reject_lit_u16_overflow, "65536u16" => None;
202 reject_lit_u32_overflow, "4294967296u32" => None;
203 reject_lit_u64_overflow, "18446744073709551616u64" => None;
204 reject_lit_u128_overflow, "340282366920938463463374607431768211456u128" => None;
205 accept_lit_i8_max, "127i8" => Some(127);
206 accept_lit_i16_max, "32767i16" => Some(32767);
207 accept_lit_i32_max, "2147483647i32" => Some(2147483647);
208 accept_lit_i64_max, "9223372036854775807i64" => Some(9223372036854775807);
209 accept_lit_i128_max, "170141183460469231731687303715884105727i128"
210 => Some(170141183460469231731687303715884105727);
211 reject_lit_i8_overflow, "128i8" => None;
212 reject_lit_i16_overflow, "32768i16" => None;
213 reject_lit_i32_overflow, "2147483648i32" => None;
214 reject_lit_i64_overflow, "9223372036854775808i64" => None;
215 reject_lit_i128_overflow, "170141183460469231731687303715884105728i128" => None;
216 accept_lit_usize, "42usize" => Some(42);
217 accept_lit_isize, "42isize" => Some(42);
218 accept_lit_byte, "b'0'" => Some(48);
219 reject_lit_negative, "-42" => None;
220 accept_add_10_20, "10 + 20" => Some(30);
221 accept_add_10u8_20u16, "10u8 + 20u16" => Some(30);
222 reject_add_overflow, "340282366920938463463374607431768211456u128 + 1" => None;
223 accept_add_commutes, "20 + 10" => Some(30);
224 accept_add_5_numbers, "(10 + 20) + 30 + (40 + 50)" => Some(150);
225 accept_add_10_0, "10 + 0" => Some(10);
226 accept_sub_20_10, "20 - 10" => Some(10);
227 reject_sub_10_20, "10 - 20" => None;
228 reject_sub_10_11, "10 - 11" => None;
229 accept_sub_10_10, "10 - 10" => Some(0);
230 accept_mul_42_0, "42 * 0" => Some(0);
231 accept_mul_0_42, "0 * 42" => Some(0);
232 accept_mul_42_1, "42 * 1" => Some(42);
233 accept_mul_1_42, "1 * 42" => Some(42);
234 accept_mul_3_4, "3 * 4" => Some(12);
235 accept_mul_4_3, "4 * 3" => Some(12);
236 accept_mul_1_2_3_4_5, "(1 * 2) * 3 * (4 * 5)" => Some(120);
237 reject_div_with_0, "10 / 0" => None;
238 accept_div_42_1, "42 / 1" => Some(42);
239 accept_div_42_42, "42 / 42" => Some(1);
240 accept_div_20_10, "20 / 10" => Some(2);
241 accept_div_10_20, "10 / 20" => Some(0);
242 reject_rem_with_0, "10 % 0" => None;
243 accept_rem_0_4, "0 % 4" => Some(0);
244 accept_rem_4_4, "4 % 4" => Some(0);
245 accept_rem_8_4, "8 % 4" => Some(0);
246 accept_rem_1_4, "1 % 4" => Some(1);
247 accept_rem_5_4, "5 % 4" => Some(1);
248 accept_rem_2_4, "2 % 4" => Some(2);
249 accept_rem_3_4, "3 % 4" => Some(3);
250 accept_xor_1, "0b0000 ^ 0b1111" => Some(0b1111);
251 accept_xor_2, "0b1111 ^ 0b0000" => Some(0b1111);
252 accept_xor_3, "0b1111 ^ 0b1111" => Some(0b0000);
253 accept_xor_4, "0b0000 ^ 0b0000" => Some(0b0000);
254 accept_xor_5, "0b1100 ^ 0b0011" => Some(0b1111);
255 accept_xor_6, "0b1001 ^ 0b1111" => Some(0b0110);
256 accept_and_1, "0b0000 & 0b0000" => Some(0b0000);
257 accept_and_2, "0b1001 & 0b0101" => Some(0b0001);
258 accept_and_3, "0b1111 & 0b1111" => Some(0b1111);
259 accept_or_1, "0b0000 | 0b0000" => Some(0b0000);
260 accept_or_2, "0b1001 | 0b0101" => Some(0b1101);
261 accept_or_3, "0b1111 | 0b1111" => Some(0b1111);
262 accept_shl, "0b001000 << 2" => Some(0b100000);
263 accept_shr, "0b001000 >> 2" => Some(0b000010);
264 accept_shl_zero, "0b001000 << 0" => Some(0b001000);
265 accept_shr_zero, "0b001000 >> 0" => Some(0b001000);
266 reject_shl_rhs_not_u32, "0b001000 << 4294967296" => None;
267 reject_shl_overflow, "0b001000 << 429496" => None;
268 reject_shr_rhs_not_u32, "0b001000 >> 4294967296" => None;
269 reject_shr_underflow, "0b001000 >> 429496" => None;
270 accept_complex_arith, "(3 + 4 * 2 - 5) / 6" => Some(1);
271 }
272}