Skip to main content

sbpf_assembler/parser/
common.rs

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
14// Shared parse functions.
15
16pub 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    // operand = { expression }, so unwrap the inner expression
40    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
52/// Evaluate an expression used as an instruction operand.
53///
54/// - A bare symbol not found in const_map or label_offset_map is returned as
55///   `Either::Left` for deferred resolution (e.g. `lddw r1, label`).
56/// - Multi-term expressions (arithmetic) resolve all symbols immediately from
57///   const_map and label_offset_map. Labels must be in the same section.
58fn 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    // Check if this is a single bare symbol (no operators)
75    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    // For a bare symbol not in const_map, defer resolution
84    if is_single_symbol {
85        if let Some(value) = const_map.get(&single_symbol_name) {
86            return Ok(Either::Right(value.clone()));
87        }
88        // Not in const_map — return as unresolved for build_program to handle
89        return Ok(Either::Left(single_symbol_name));
90    }
91
92    // Multi-term expression: resolve everything now
93    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    // Bounds check: all labels in the expression must be from the same section
114    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    // Evaluate left-to-right
129    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                // Parenthesized sub-expression — recurse, but must fully resolve
179                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    // Try parsing as i64 first
306    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
332// Shared process functions.
333
334pub 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                // Extract opcode and size from instruction (example: "be16" = be opcode, 16 bits)
386                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}