neo_solidity/ir/build/
literals.rs1fn literal_from_expression(expr: &Expression) -> Option<LiteralValue> {
2 match expr {
3 Expression::BoolLiteral(_, value) => Some(LiteralValue::Boolean(*value)),
4 Expression::NumberLiteral(_, integer, exp, unit) => {
5 let mut value = parse_decimal_bigint(integer)?;
6 let exponent = parse_signed_decimal_i32(exp)?;
7
8 if exponent >= 0 {
9 value *= pow10(exponent as u32);
10 } else {
11 let divisor = pow10((-exponent) as u32);
12 if (&value % &divisor).is_zero() {
13 value /= divisor;
14 } else {
15 return None;
16 }
17 }
18
19 if let Some(unit) = unit.as_ref() {
20 value *= unit_multiplier(unit)?;
21 }
22
23 Some(LiteralValue::Integer(value))
24 }
25 Expression::HexNumberLiteral(_, value, unit) => {
26 let mut number = parse_hex_bigint(value)?;
27 if let Some(unit) = unit.as_ref() {
28 number *= unit_multiplier(unit)?;
29 }
30 Some(LiteralValue::Integer(number))
31 }
32 Expression::RationalNumberLiteral(_, integer, fraction, exp, unit) => {
33 let int_part = parse_decimal_bigint(integer)?;
34 let fraction_digits = sanitize_numeric_token(fraction);
35 let frac_len = fraction_digits.len() as u32;
36 let frac_part = if fraction_digits.trim().is_empty() {
37 BigInt::zero()
38 } else {
39 BigInt::parse_bytes(fraction_digits.as_bytes(), 10)?
40 };
41
42 let mut numerator = int_part * pow10(frac_len) + frac_part;
43 let mut denominator = pow10(frac_len);
44
45 let exponent = parse_signed_decimal_i32(exp)?;
46 if exponent >= 0 {
47 numerator *= pow10(exponent as u32);
48 } else {
49 denominator *= pow10((-exponent) as u32);
50 }
51
52 if let Some(unit) = unit.as_ref() {
53 numerator *= unit_multiplier(unit)?;
54 }
55
56 if denominator.is_zero() {
57 return None;
58 }
59
60 if (&numerator % &denominator).is_zero() {
61 Some(LiteralValue::Integer(numerator / denominator))
62 } else {
63 eprintln!(
64 "warning: non-integer rational literal {}.{} cannot be represented \
65 as an integer; fractional values are not supported on NeoVM",
66 integer, fraction
67 );
68 None
69 }
70 }
71 Expression::StringLiteral(parts) => Some(LiteralValue::String(string_literal_bytes(parts))),
72 Expression::HexLiteral(parts) => decode_hex_segments(parts).map(LiteralValue::ByteArray),
73 Expression::AddressLiteral(_, value) => decode_hex_bytes(value).and_then(|mut bytes| {
74 if bytes.len() != 20 {
76 eprintln!(
77 "warning: address literal has {} bytes, expected 20 (UInt160)",
78 bytes.len()
79 );
80 return None;
81 }
82 bytes.reverse();
86 Some(LiteralValue::Address(bytes))
87 }),
88 Expression::Parenthesis(_, inner) => literal_from_expression(inner),
89 _ => None,
90 }
91}
92
93fn address_bytes_le_from_expression(expr: &Expression) -> Option<Vec<u8>> {
94 match expr {
95 Expression::Parenthesis(_, inner) => address_bytes_le_from_expression(inner),
96 Expression::AddressLiteral(_, value) => decode_hex_bytes(value).map(|mut bytes| {
97 if bytes.len() > 20 {
98 bytes.truncate(20);
99 } else if bytes.len() < 20 {
100 let mut padded = vec![0u8; 20 - bytes.len()];
101 padded.extend_from_slice(&bytes);
102 bytes = padded;
103 }
104 bytes.reverse();
105 bytes
106 }),
107 Expression::HexNumberLiteral(_, value, unit) if unit.is_none() => {
108 let raw = value.trim().trim_start_matches("0x");
109 let mut hex: String = raw
110 .chars()
111 .filter(|c| !c.is_whitespace() && *c != '_')
112 .collect();
113 if hex.is_empty() {
114 return None;
115 }
116
117 if hex.len() > 40 {
118 return None;
119 }
120
121 if hex.len() % 2 == 1 {
122 hex.insert(0, '0');
123 }
124
125 if hex.len() < 40 {
126 hex = format!("{:0>40}", hex);
127 }
128
129 let mut bytes = hex_decode(&hex).ok()?;
130 if bytes.len() != 20 {
131 return None;
132 }
133 bytes.reverse();
134 Some(bytes)
135 }
136 _ => None,
137 }
138}
139
140fn decode_hex_segments(parts: &[PtHexLiteral]) -> Option<Vec<u8>> {
141 let mut bytes = Vec::new();
142 for part in parts {
143 let segment = part.hex.trim();
144 let inner = segment
145 .strip_prefix("hex")
146 .and_then(|s| s.trim().strip_prefix('\"'))
147 .and_then(|s| s.strip_suffix('\"'))
148 .unwrap_or(segment);
149 let cleaned: String = inner.chars().filter(|c| !c.is_whitespace()).collect();
150 bytes.extend(hex_decode(&cleaned).ok()?);
151 }
152 Some(bytes)
153}
154
155fn decode_hex_bytes(value: &str) -> Option<Vec<u8>> {
156 let cleaned = value.trim();
157 if let Some(inner) = cleaned.strip_prefix("0x") {
158 hex_decode(inner).ok()
159 } else {
160 hex_decode(cleaned).ok()
161 }
162}
163
164fn parse_decimal_bigint(value: &str) -> Option<BigInt> {
165 let sanitized: String = value.chars().filter(|c| *c != '_').collect();
166 BigInt::parse_bytes(sanitized.as_bytes(), 10)
167}
168
169fn sanitize_numeric_token(value: &str) -> String {
170 value.chars().filter(|c| *c != '_').collect()
171}
172
173fn parse_signed_decimal_i32(value: &str) -> Option<i32> {
174 let sanitized = sanitize_numeric_token(value);
175 if sanitized.trim().is_empty() {
176 Some(0)
177 } else {
178 sanitized.parse::<i32>().ok()
179 }
180}
181
182fn unit_multiplier(unit: &Identifier) -> Option<BigInt> {
183 match unit.name.as_str() {
184 "wei" => Some(BigInt::one()),
186 "gwei" => Some(pow10(9)),
187 "szabo" => Some(pow10(12)),
188 "finney" => Some(pow10(15)),
189 "ether" => Some(pow10(18)),
190
191 "second" | "seconds" => Some(BigInt::one()),
194 "minute" | "minutes" => Some(BigInt::from(60u64)),
195 "hour" | "hours" => Some(BigInt::from(60u64 * 60)),
196 "day" | "days" => Some(BigInt::from(60u64 * 60 * 24)),
197 "week" | "weeks" => Some(BigInt::from(60u64 * 60 * 24 * 7)),
198 "year" | "years" => Some(BigInt::from(60u64 * 60 * 24 * 365)),
199
200 _ => None,
201 }
202}
203
204fn is_ether_unit(name: &str) -> bool {
205 matches!(name, "wei" | "gwei" | "szabo" | "finney" | "ether")
206}
207
208fn has_ether_unit(expr: &Expression) -> bool {
209 match expr {
210 Expression::NumberLiteral(_, _, _, Some(unit)) => is_ether_unit(&unit.name),
211 Expression::HexNumberLiteral(_, _, Some(unit)) => is_ether_unit(&unit.name),
212 Expression::RationalNumberLiteral(_, _, _, _, Some(unit)) => is_ether_unit(&unit.name),
213 Expression::Parenthesis(_, inner) => has_ether_unit(inner),
214 _ => false,
215 }
216}
217
218fn pow10(exp: u32) -> BigInt {
219 let ten = BigInt::from(10u8);
220 ten.pow(exp)
221}
222
223fn parse_hex_bigint(value: &str) -> Option<BigInt> {
224 let sanitized = value.trim_start_matches("0x");
225 BigInt::parse_bytes(sanitized.as_bytes(), 16)
226}