Skip to main content

sbpf_assembler/parser/
common.rs

1use {
2    super::{Rule, Section},
3    crate::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].clone();
141            result = match *op {
142                "+" => result + rhs,
143                "-" => result - rhs,
144                "*" => result * rhs,
145                "/" => result / rhs,
146                _ => result,
147            };
148        }
149    }
150
151    Ok(Either::Right(result))
152}
153
154fn eval_operand_term(
155    pair: Pair<Rule>,
156    const_map: &HashMap<String, Number>,
157    label_offset_map: &HashMap<String, (Number, Section)>,
158    label_sections: &mut Vec<(String, Section)>,
159) -> Result<Number, CompileError> {
160    let span = pair.as_span();
161    let span_range = span.start()..span.end();
162
163    for inner in pair.into_inner() {
164        match inner.as_rule() {
165            Rule::expression => {
166                // Parenthesized sub-expression — recurse, but must fully resolve
167                let result = eval_operand_expression(inner, const_map, label_offset_map)?;
168                return match result {
169                    Either::Right(val) => Ok(val),
170                    Either::Left(name) => Err(CompileError::ParseError {
171                        error: format!(
172                            "Cannot use unresolved symbol '{}' in arithmetic expression",
173                            name
174                        ),
175                        span: span_range,
176                        custom_label: None,
177                    }),
178                };
179            }
180            Rule::number => {
181                return parse_number(inner);
182            }
183            Rule::symbol => {
184                let name = inner.as_str().to_string();
185                if let Some(value) = const_map.get(&name) {
186                    return Ok(value.clone());
187                }
188                if let Some((value, section)) = label_offset_map.get(&name) {
189                    label_sections.push((name, *section));
190                    return Ok(value.clone());
191                }
192                return Err(CompileError::ParseError {
193                    error: format!("Undefined symbol '{}' in arithmetic expression", name),
194                    span: inner.as_span().start()..inner.as_span().end(),
195                    custom_label: None,
196                });
197            }
198            _ => {}
199        }
200    }
201
202    Err(CompileError::ParseError {
203        error: "Invalid term in expression".to_string(),
204        span: span_range,
205        custom_label: None,
206    })
207}
208
209pub fn parse_jump_target(
210    pair: Pair<Rule>,
211    _const_map: &HashMap<String, Number>,
212) -> Result<Either<String, i16>, CompileError> {
213    let span = pair.as_span();
214    let span_range = span.start()..span.end();
215
216    for inner in pair.into_inner() {
217        match inner.as_rule() {
218            Rule::symbol | Rule::numeric_label_ref => {
219                return Ok(Either::Left(inner.as_str().to_string()));
220            }
221            Rule::number | Rule::signed_number => {
222                let num = parse_number(inner)?;
223                return Ok(Either::Right(num.to_i16()));
224            }
225            _ => {}
226        }
227    }
228
229    Err(CompileError::ParseError {
230        error: "Invalid jump target".to_string(),
231        span: span_range,
232        custom_label: None,
233    })
234}
235
236pub fn parse_memory_ref(
237    pair: Pair<Rule>,
238    const_map: &HashMap<String, Number>,
239) -> Result<(Register, Either<String, i16>), CompileError> {
240    let mut reg = None;
241    let mut accumulated_offset: i16 = 0;
242    let mut unresolved_symbol: Option<String> = None;
243    let mut sign: i16 = 1;
244
245    for inner in pair.into_inner() {
246        match inner.as_rule() {
247            Rule::register => {
248                reg = Some(parse_register(inner)?);
249            }
250            Rule::memory_op => {
251                sign = if inner.as_str() == "+" { 1 } else { -1 };
252            }
253            Rule::memory_offset => {
254                for offset_inner in inner.into_inner() {
255                    match offset_inner.as_rule() {
256                        Rule::number => {
257                            let num = parse_number(offset_inner)?;
258                            accumulated_offset =
259                                accumulated_offset.wrapping_add(sign * num.to_i16());
260                        }
261                        Rule::symbol => {
262                            let name = offset_inner.as_str().to_string();
263                            if let Some(value) = const_map.get(&name) {
264                                accumulated_offset =
265                                    accumulated_offset.wrapping_add(sign * value.to_i16());
266                            } else if unresolved_symbol.is_none() {
267                                unresolved_symbol = Some(name);
268                            }
269                        }
270                        _ => {}
271                    }
272                }
273            }
274            _ => {}
275        }
276    }
277
278    let offset = if let Some(sym) = unresolved_symbol {
279        Either::Left(sym)
280    } else {
281        Either::Right(accumulated_offset)
282    };
283
284    Ok((reg.unwrap_or(Register { n: 0 }), offset))
285}
286
287pub fn parse_number(pair: Pair<Rule>) -> Result<Number, CompileError> {
288    let span = pair.as_span();
289    let span_range = span.start()..span.end();
290    let raw = pair.as_str();
291    let number_str = raw.strip_prefix('+').unwrap_or(raw).replace('_', "");
292
293    // Try parsing as i64 first
294    if let Ok(value) = number_str.parse::<i64>() {
295        return Ok(Number::Int(value));
296    }
297
298    let mut sign: i64 = 1;
299    let value = if number_str.starts_with('-') {
300        sign = -1;
301        number_str.strip_prefix('-').unwrap()
302    } else {
303        number_str.as_str()
304    };
305
306    if value.starts_with("0x") {
307        let hex_str = value.trim_start_matches("0x");
308        if let Ok(value) = u64::from_str_radix(hex_str, 16) {
309            return Ok(Number::Addr(sign * (value as i64)));
310        }
311    }
312
313    Err(CompileError::InvalidNumber {
314        number: number_str,
315        span: span_range,
316        custom_label: None,
317    })
318}
319
320// Shared process functions.
321
322pub fn process_exit(span: std::ops::Range<usize>) -> Result<Instruction, CompileError> {
323    Ok(Instruction {
324        opcode: Opcode::Exit,
325        dst: None,
326        src: None,
327        off: None,
328        imm: None,
329        span,
330    })
331}
332
333pub(crate) fn process_lddw(
334    pair: Pair<Rule>,
335    const_map: &HashMap<String, Number>,
336    label_offset_map: &HashMap<String, (Number, Section)>,
337    span: std::ops::Range<usize>,
338) -> Result<Instruction, CompileError> {
339    let mut dst = None;
340    let mut imm = None;
341
342    for inner in pair.into_inner() {
343        match inner.as_rule() {
344            Rule::register => dst = Some(parse_register(inner)?),
345            Rule::operand => imm = Some(parse_operand(inner, const_map, label_offset_map)?),
346            _ => {}
347        }
348    }
349
350    Ok(Instruction {
351        opcode: Opcode::Lddw,
352        dst,
353        src: None,
354        off: None,
355        imm,
356        span,
357    })
358}
359
360pub fn process_endian(
361    pair: Pair<Rule>,
362    span: std::ops::Range<usize>,
363) -> Result<Instruction, CompileError> {
364    let mut opcode = None;
365    let mut dst = None;
366    let mut imm = None;
367
368    for inner in pair.into_inner() {
369        match inner.as_rule() {
370            Rule::endian_op => {
371                let op_str = inner.as_str();
372                let inner_span = inner.as_span();
373                // Extract opcode and size from instruction (example: "be16" = be opcode, 16 bits)
374                let (opc, size) = if let Some(size_str) = op_str.strip_prefix("be") {
375                    let size = size_str
376                        .parse::<i64>()
377                        .map_err(|_| CompileError::ParseError {
378                            error: format!("Invalid endian size in '{}'", op_str),
379                            span: inner_span.start()..inner_span.end(),
380                            custom_label: None,
381                        })?;
382                    (Opcode::Be, size)
383                } else if let Some(size_str) = op_str.strip_prefix("le") {
384                    let size = size_str
385                        .parse::<i64>()
386                        .map_err(|_| CompileError::ParseError {
387                            error: format!("Invalid endian size in '{}'", op_str),
388                            span: inner_span.start()..inner_span.end(),
389                            custom_label: None,
390                        })?;
391                    (Opcode::Le, size)
392                } else {
393                    return Err(CompileError::ParseError {
394                        error: format!("Invalid endian operation '{}'", op_str),
395                        span: inner_span.start()..inner_span.end(),
396                        custom_label: None,
397                    });
398                };
399                opcode = Some(opc);
400                imm = Some(Either::Right(Number::Int(size)));
401            }
402            Rule::register => dst = Some(parse_register(inner)?),
403            _ => {}
404        }
405    }
406
407    Ok(Instruction {
408        opcode: opcode.unwrap_or(Opcode::Exit),
409        dst,
410        src: None,
411        off: None,
412        imm,
413        span,
414    })
415}
416
417pub fn process_call(
418    pair: Pair<Rule>,
419    const_map: &HashMap<String, Number>,
420    span: std::ops::Range<usize>,
421) -> Result<Instruction, CompileError> {
422    let mut imm = None;
423
424    for inner in pair.into_inner() {
425        if inner.as_rule() == Rule::symbol {
426            if let Some(symbol) = const_map.get(inner.as_str()) {
427                imm = Some(Either::Right(symbol.to_owned()));
428            } else {
429                imm = Some(Either::Left(inner.as_str().to_string()));
430            }
431        }
432    }
433
434    Ok(Instruction {
435        opcode: Opcode::Call,
436        dst: None,
437        src: None,
438        off: None,
439        imm,
440        span,
441    })
442}
443
444pub fn process_callx(
445    pair: Pair<Rule>,
446    span: std::ops::Range<usize>,
447) -> Result<Instruction, CompileError> {
448    let mut dst = None;
449
450    for inner in pair.into_inner() {
451        if inner.as_rule() == Rule::register {
452            dst = Some(parse_register(inner)?);
453        }
454    }
455
456    Ok(Instruction {
457        opcode: Opcode::Callx,
458        dst,
459        src: None,
460        off: None,
461        imm: None,
462        span,
463    })
464}