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
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].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 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 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
320pub 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 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}