1use {
2 super::{Rule, Section},
3 crate::{SbpfArch, errors::CompileError},
4 either::Either,
5 pest::iterators::Pair,
6 sbpf_common::{
7 inst_param::{Number, Register},
8 instruction::Instruction,
9 opcode::Opcode,
10 },
11 std::collections::HashMap,
12};
13
14pub fn parse_register(pair: Pair<Rule>) -> Result<Register, CompileError> {
17 let reg_str = pair.as_str();
18 let span = pair.as_span();
19
20 if let Ok(n) = reg_str[1..].parse::<u8>() {
21 Ok(Register { n })
22 } else {
23 Err(CompileError::InvalidRegister {
24 register: reg_str.to_string(),
25 span: span.start()..span.end(),
26 custom_label: None,
27 })
28 }
29}
30
31pub(crate) fn parse_operand(
32 pair: Pair<Rule>,
33 const_map: &HashMap<String, Number>,
34 label_offset_map: &HashMap<String, (Number, Section)>,
35) -> Result<Either<String, Number>, CompileError> {
36 let span = pair.as_span();
37 let span_range = span.start()..span.end();
38
39 let expr = pair
41 .into_inner()
42 .next()
43 .ok_or_else(|| CompileError::ParseError {
44 error: "Invalid operand".to_string(),
45 span: span_range.clone(),
46 custom_label: None,
47 })?;
48
49 eval_operand_expression(expr, const_map, label_offset_map)
50}
51
52fn eval_operand_expression(
59 pair: Pair<Rule>,
60 const_map: &HashMap<String, Number>,
61 label_offset_map: &HashMap<String, (Number, Section)>,
62) -> Result<Either<String, Number>, CompileError> {
63 let span = pair.as_span();
64 let span_range = span.start()..span.end();
65
66 let mut terms: Vec<Number> = Vec::new();
67 let mut ops: Vec<&str> = Vec::new();
68 let mut is_single_symbol = false;
69 let mut single_symbol_name = String::new();
70 let mut label_sections: Vec<(String, Section)> = Vec::new();
71
72 let inner_pairs: Vec<_> = pair.into_inner().collect();
73
74 if inner_pairs.len() == 1 && inner_pairs[0].as_rule() == Rule::term {
76 let term_inners: Vec<_> = inner_pairs[0].clone().into_inner().collect();
77 if term_inners.len() == 1 && term_inners[0].as_rule() == Rule::symbol {
78 is_single_symbol = true;
79 single_symbol_name = term_inners[0].as_str().to_string();
80 }
81 }
82
83 if is_single_symbol {
85 if let Some(value) = const_map.get(&single_symbol_name) {
86 return Ok(Either::Right(value.clone()));
87 }
88 return Ok(Either::Left(single_symbol_name));
90 }
91
92 for inner in inner_pairs {
94 match inner.as_rule() {
95 Rule::term => {
96 let val =
97 eval_operand_term(inner, const_map, label_offset_map, &mut label_sections)?;
98 terms.push(val);
99 }
100 Rule::bin_op => {
101 ops.push(match inner.as_str() {
102 "+" => "+",
103 "-" => "-",
104 "*" => "*",
105 "/" => "/",
106 _ => "+",
107 });
108 }
109 _ => {}
110 }
111 }
112
113 if label_sections.len() > 1 {
115 let first_section = label_sections[0].1;
116 for (name, section) in &label_sections[1..] {
117 if *section != first_section {
118 return Err(CompileError::CrossSectionArithmetic {
119 label1: label_sections[0].0.clone(),
120 label2: name.clone(),
121 span: span_range,
122 custom_label: None,
123 });
124 }
125 }
126 }
127
128 if terms.is_empty() {
130 return Err(CompileError::ParseError {
131 error: "Invalid operand expression".to_string(),
132 span: span_range,
133 custom_label: None,
134 });
135 }
136
137 let mut result = terms[0].clone();
138 for (i, op) in ops.iter().enumerate() {
139 if i + 1 < terms.len() {
140 let rhs = &terms[i + 1];
141 let folded = match *op {
142 "+" => result.checked_add(rhs),
143 "-" => result.checked_sub(rhs),
144 "*" => result.checked_mul(rhs),
145 "/" => result.checked_div(rhs),
146 _ => Some(result.clone()),
147 };
148 result = folded.ok_or_else(|| {
149 let detail = if *op == "/" && rhs.to_i64() == 0 {
150 "division by zero in constant expression".to_string()
151 } else {
152 format!("arithmetic overflow in constant expression ('{op}')")
153 };
154 CompileError::ArithmeticError {
155 error: detail,
156 span: span_range.clone(),
157 custom_label: None,
158 }
159 })?;
160 }
161 }
162
163 Ok(Either::Right(result))
164}
165
166fn eval_operand_term(
167 pair: Pair<Rule>,
168 const_map: &HashMap<String, Number>,
169 label_offset_map: &HashMap<String, (Number, Section)>,
170 label_sections: &mut Vec<(String, Section)>,
171) -> Result<Number, CompileError> {
172 let span = pair.as_span();
173 let span_range = span.start()..span.end();
174
175 for inner in pair.into_inner() {
176 match inner.as_rule() {
177 Rule::expression => {
178 let result = eval_operand_expression(inner, const_map, label_offset_map)?;
180 return match result {
181 Either::Right(val) => Ok(val),
182 Either::Left(name) => Err(CompileError::ParseError {
183 error: format!(
184 "Cannot use unresolved symbol '{}' in arithmetic expression",
185 name
186 ),
187 span: span_range,
188 custom_label: None,
189 }),
190 };
191 }
192 Rule::number => {
193 return parse_number(inner);
194 }
195 Rule::symbol => {
196 let name = inner.as_str().to_string();
197 if let Some(value) = const_map.get(&name) {
198 return Ok(value.clone());
199 }
200 if let Some((value, section)) = label_offset_map.get(&name) {
201 label_sections.push((name, *section));
202 return Ok(value.clone());
203 }
204 return Err(CompileError::ParseError {
205 error: format!("Undefined symbol '{}' in arithmetic expression", name),
206 span: inner.as_span().start()..inner.as_span().end(),
207 custom_label: None,
208 });
209 }
210 _ => {}
211 }
212 }
213
214 Err(CompileError::ParseError {
215 error: "Invalid term in expression".to_string(),
216 span: span_range,
217 custom_label: None,
218 })
219}
220
221pub fn parse_jump_target(
222 pair: Pair<Rule>,
223 _const_map: &HashMap<String, Number>,
224) -> Result<Either<String, i16>, CompileError> {
225 let span = pair.as_span();
226 let span_range = span.start()..span.end();
227
228 for inner in pair.into_inner() {
229 match inner.as_rule() {
230 Rule::symbol | Rule::numeric_label_ref => {
231 return Ok(Either::Left(inner.as_str().to_string()));
232 }
233 Rule::number | Rule::signed_number => {
234 let num = parse_number(inner)?;
235 return Ok(Either::Right(num.to_i16()));
236 }
237 _ => {}
238 }
239 }
240
241 Err(CompileError::ParseError {
242 error: "Invalid jump target".to_string(),
243 span: span_range,
244 custom_label: None,
245 })
246}
247
248pub fn parse_memory_ref(
249 pair: Pair<Rule>,
250 const_map: &HashMap<String, Number>,
251) -> Result<(Register, Either<String, i16>), CompileError> {
252 let mut reg = None;
253 let mut accumulated_offset: i16 = 0;
254 let mut unresolved_symbol: Option<String> = None;
255 let mut sign: i16 = 1;
256
257 for inner in pair.into_inner() {
258 match inner.as_rule() {
259 Rule::register => {
260 reg = Some(parse_register(inner)?);
261 }
262 Rule::memory_op => {
263 sign = if inner.as_str() == "+" { 1 } else { -1 };
264 }
265 Rule::memory_offset => {
266 for offset_inner in inner.into_inner() {
267 match offset_inner.as_rule() {
268 Rule::number => {
269 let num = parse_number(offset_inner)?;
270 accumulated_offset =
271 accumulated_offset.wrapping_add(sign * num.to_i16());
272 }
273 Rule::symbol => {
274 let name = offset_inner.as_str().to_string();
275 if let Some(value) = const_map.get(&name) {
276 accumulated_offset =
277 accumulated_offset.wrapping_add(sign * value.to_i16());
278 } else if unresolved_symbol.is_none() {
279 unresolved_symbol = Some(name);
280 }
281 }
282 _ => {}
283 }
284 }
285 }
286 _ => {}
287 }
288 }
289
290 let offset = if let Some(sym) = unresolved_symbol {
291 Either::Left(sym)
292 } else {
293 Either::Right(accumulated_offset)
294 };
295
296 Ok((reg.unwrap_or(Register { n: 0 }), offset))
297}
298
299pub fn parse_number(pair: Pair<Rule>) -> Result<Number, CompileError> {
300 let span = pair.as_span();
301 let span_range = span.start()..span.end();
302 let raw = pair.as_str();
303 let number_str = raw.strip_prefix('+').unwrap_or(raw).replace('_', "");
304
305 if let Ok(value) = number_str.parse::<i64>() {
307 return Ok(Number::Int(value));
308 }
309
310 let mut sign: i64 = 1;
311 let value = if number_str.starts_with('-') {
312 sign = -1;
313 number_str.strip_prefix('-').unwrap()
314 } else {
315 number_str.as_str()
316 };
317
318 if value.starts_with("0x") {
319 let hex_str = value.trim_start_matches("0x");
320 if let Ok(value) = u64::from_str_radix(hex_str, 16) {
321 return Ok(Number::Addr(sign * (value as i64)));
322 }
323 }
324
325 Err(CompileError::InvalidNumber {
326 number: number_str,
327 span: span_range,
328 custom_label: None,
329 })
330}
331
332pub fn process_exit(span: std::ops::Range<usize>) -> Result<Instruction, CompileError> {
335 Ok(Instruction {
336 opcode: Opcode::Exit,
337 dst: None,
338 src: None,
339 off: None,
340 imm: None,
341 span,
342 })
343}
344
345pub(crate) fn process_lddw(
346 pair: Pair<Rule>,
347 const_map: &HashMap<String, Number>,
348 label_offset_map: &HashMap<String, (Number, Section)>,
349 span: std::ops::Range<usize>,
350) -> Result<Instruction, CompileError> {
351 let mut dst = None;
352 let mut imm = None;
353
354 for inner in pair.into_inner() {
355 match inner.as_rule() {
356 Rule::register => dst = Some(parse_register(inner)?),
357 Rule::operand => imm = Some(parse_operand(inner, const_map, label_offset_map)?),
358 _ => {}
359 }
360 }
361
362 Ok(Instruction {
363 opcode: Opcode::Lddw,
364 dst,
365 src: None,
366 off: None,
367 imm,
368 span,
369 })
370}
371
372pub fn process_endian(
373 pair: Pair<Rule>,
374 span: std::ops::Range<usize>,
375) -> Result<Instruction, CompileError> {
376 let mut opcode = None;
377 let mut dst = None;
378 let mut imm = None;
379
380 for inner in pair.into_inner() {
381 match inner.as_rule() {
382 Rule::endian_op => {
383 let op_str = inner.as_str();
384 let inner_span = inner.as_span();
385 let (opc, size) = if let Some(size_str) = op_str.strip_prefix("be") {
387 let size = size_str
388 .parse::<i64>()
389 .map_err(|_| CompileError::ParseError {
390 error: format!("Invalid endian size in '{}'", op_str),
391 span: inner_span.start()..inner_span.end(),
392 custom_label: None,
393 })?;
394 (Opcode::Be, size)
395 } else if let Some(size_str) = op_str.strip_prefix("le") {
396 let size = size_str
397 .parse::<i64>()
398 .map_err(|_| CompileError::ParseError {
399 error: format!("Invalid endian size in '{}'", op_str),
400 span: inner_span.start()..inner_span.end(),
401 custom_label: None,
402 })?;
403 (Opcode::Le, size)
404 } else {
405 return Err(CompileError::ParseError {
406 error: format!("Invalid endian operation '{}'", op_str),
407 span: inner_span.start()..inner_span.end(),
408 custom_label: None,
409 });
410 };
411 opcode = Some(opc);
412 imm = Some(Either::Right(Number::Int(size)));
413 }
414 Rule::register => dst = Some(parse_register(inner)?),
415 _ => {}
416 }
417 }
418
419 Ok(Instruction {
420 opcode: opcode.unwrap_or(Opcode::Exit),
421 dst,
422 src: None,
423 off: None,
424 imm,
425 span,
426 })
427}
428
429pub fn process_call(
430 pair: Pair<Rule>,
431 const_map: &HashMap<String, Number>,
432 span: std::ops::Range<usize>,
433) -> Result<Instruction, CompileError> {
434 let mut imm = None;
435
436 for inner in pair.into_inner() {
437 if inner.as_rule() == Rule::symbol {
438 if let Some(symbol) = const_map.get(inner.as_str()) {
439 imm = Some(Either::Right(symbol.to_owned()));
440 } else {
441 imm = Some(Either::Left(inner.as_str().to_string()));
442 }
443 }
444 }
445
446 Ok(Instruction {
447 opcode: Opcode::Call,
448 dst: None,
449 src: None,
450 off: None,
451 imm,
452 span,
453 })
454}
455
456pub fn process_callx(
457 pair: Pair<Rule>,
458 span: std::ops::Range<usize>,
459) -> Result<Instruction, CompileError> {
460 let mut dst = None;
461
462 for inner in pair.into_inner() {
463 if inner.as_rule() == Rule::register {
464 dst = Some(parse_register(inner)?);
465 }
466 }
467
468 Ok(Instruction {
469 opcode: Opcode::Callx,
470 dst,
471 src: None,
472 off: None,
473 imm: None,
474 span,
475 })
476}
477
478pub(crate) fn check_arch_v3(pair: &Pair<Rule>, arch: SbpfArch) -> Result<(), CompileError> {
479 if arch.is_v3() {
480 return Ok(());
481 }
482
483 if let Some(inner) = pair.clone().into_inner().next() {
484 let span = inner.as_span();
485 return Err(CompileError::ParseError {
486 error: format!("instruction '{}' requires arch v3", inner.as_str()),
487 span: span.start()..span.end(),
488 custom_label: None,
489 });
490 }
491
492 Ok(())
493}