From 25b921a1bf708456ff6d1227e39c035be897bab5 Mon Sep 17 00:00:00 2001 From: xarantolus Date: Thu, 21 Mar 2024 08:31:33 +0100 Subject: [PATCH] Evaluate memory region expressions --- src/eval.rs | 87 +++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/memory.rs | 61 +++++++++++++++++++++++++++---- src/script.rs | 7 ++++ tests/bootloader.ld | 6 ++++ 5 files changed, 156 insertions(+), 6 deletions(-) create mode 100644 src/eval.rs create mode 100644 tests/bootloader.ld diff --git a/src/eval.rs b/src/eval.rs new file mode 100644 index 0000000..201c2fa --- /dev/null +++ b/src/eval.rs @@ -0,0 +1,87 @@ +use crate::expressions::{BinaryOperator, Expression}; + +pub fn evaluate_expression(expr: Expression) -> Result { + Ok(match expr { + Expression::Number(n) => n, + Expression::BinaryOp { + left, + operator, + right, + } => { + let left = evaluate_expression(*left)?; + let right = evaluate_expression(*right)?; + match operator { + BinaryOperator::Plus => left.wrapping_add(right), + BinaryOperator::Minus => left.wrapping_sub(right), + BinaryOperator::Multiply => left.wrapping_mul(right), + BinaryOperator::Divide => left.wrapping_div(right), + _ => return Err(format!("Binary operator {:?} not supported", operator)), + } + } + _ => return Err(format!("Expression {:?} not supported", expr)), + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use nom::combinator::map_res; + use BinaryOperator::*; + + #[test] + fn test_evaluate_expression() { + assert_eq!(evaluate_expression(Expression::Number(42)), Ok(42)); + + assert_eq!( + evaluate_expression(Expression::BinaryOp { + left: Box::new(Expression::Number(42)), + operator: Plus, + right: Box::new(Expression::Number(42)) + }), + Ok(84) + ); + assert_eq!( + evaluate_expression(Expression::BinaryOp { + left: Box::new(Expression::Number(42)), + operator: Minus, + right: Box::new(Expression::Number(42)) + }), + Ok(0) + ); + assert_eq!( + evaluate_expression(Expression::BinaryOp { + left: Box::new(Expression::Number(42)), + operator: Multiply, + right: Box::new(Expression::Number(42)) + }), + Ok(1764) + ); + assert_eq!( + evaluate_expression(Expression::BinaryOp { + left: Box::new(Expression::Number(42)), + operator: Divide, + right: Box::new(Expression::Number(42)) + }), + Ok(1) + ); + } + + fn expr_result(input: &str, expected: u64) { + assert_done!( + map_res(crate::expressions::expression, evaluate_expression)(input), + expected + ); + } + + #[test] + fn test_parsed_expressions() { + expr_result("42 - (20 + 21)", 1); + expr_result("42 - (4 * 8)", 10); + expr_result("42", 42); + expr_result("42 + 42", 84); + expr_result("42 - 42", 0); + expr_result("42 * 42", 1764); + expr_result("42 / 42", 1); + expr_result("0x2000000 + (4k * 4)", 0x2000000 + (4 * 1024 * 4)); + } +} diff --git a/src/lib.rs b/src/lib.rs index e5fa9bd..f1f3578 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,6 +29,7 @@ mod utils; #[macro_use] mod whitespace; mod commands; +mod eval; mod expressions; mod idents; mod memory; diff --git a/src/memory.rs b/src/memory.rs index 8b30cc8..6f69944 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -2,13 +2,14 @@ use idents::symbol; use nom::{ branch::alt, bytes::complete::{tag, take_until}, - combinator::opt, + combinator::{map_res, opt}, sequence::{delimited, tuple}, IResult, }; -use numbers::number; use whitespace::opt_space; +use crate::{eval::evaluate_expression, expressions::expression}; + #[derive(Debug, PartialEq)] pub struct Region { pub name: String, @@ -37,15 +38,15 @@ pub fn region(input: &str) -> IResult<&str, Region> { origin, wsc!(tag("=")), ))(input)?; - let (input, org) = number(input)?; + let (input, origin) = map_res(expression, evaluate_expression)(input)?; let (input, _) = tuple((wsc!(tag(",")), length, wsc!(tag("="))))(input)?; - let (input, len) = number(input)?; + let (input, length) = map_res(expression, evaluate_expression)(input)?; Ok(( input, Region { name: name.into(), - origin: org, - length: len, + origin, + length, }, )) } @@ -73,4 +74,52 @@ mod tests { } ); } + + #[test] + fn test_region_expr() { + assert_done!( + region("FLASH : ORIGIN = 0x08000000, LENGTH = 8K"), + Region { + name: "FLASH".into(), + origin: 0x08000000, + length: 8 * 1024, + } + ); + + assert_done!( + region("RAM : ORIGIN = 0x20000000 + 8K, LENGTH = 640K"), + Region { + name: "RAM".into(), + origin: 0x20000000 + 8 * 1024, + length: 640 * 1024, + } + ); + + assert_done!( + region("RAM : ORIGIN = 0x20000000, LENGTH = 640K - 8K"), + Region { + name: "RAM".into(), + origin: 0x20000000, + length: 640 * 1024 - 8 * 1024, + } + ); + + assert_done!( + region("RAM : ORIGIN = 0x20000000 + 8K - 4K, LENGTH = 640K - 8K + 4K"), + Region { + name: "RAM".into(), + origin: 0x20000000 + 4 * 1024, + length: 640 * 1024 - 4 * 1024, + } + ); + + assert_done!( + region("RAM: ORIGIN = 0x20000000 + 8K - 4K, LENGTH = 640K - 8K + 4K"), + Region { + name: "RAM".into(), + origin: 0x20000000 + 4 * 1024, + length: 640 * 1024 - 4 * 1024, + } + ); + } } diff --git a/src/script.rs b/src/script.rs index 2cdc9d9..386f915 100644 --- a/src/script.rs +++ b/src/script.rs @@ -63,6 +63,13 @@ mod tests { assert_done_vec!(parse(" /* hello */ "), 0); } + #[test] + fn test_bootloader() { + let input = include_str!("../tests/bootloader.ld"); + let res = parse(&input); + assert!(!res.unwrap().1.is_empty()); + } + #[test] fn test_parse() { for entry in fs::read_dir("tests").unwrap() { diff --git a/tests/bootloader.ld b/tests/bootloader.ld new file mode 100644 index 0000000..6ce7eb3 --- /dev/null +++ b/tests/bootloader.ld @@ -0,0 +1,6 @@ +MEMORY +{ + FLASH : ORIGIN = 0x08000000, LENGTH = 8K + + RAM : ORIGIN = 0x20000000 + 256K, LENGTH = 640K - 256K +}