avra_lib/
expr.rs

1//! Contains expression representation of AVRA-rs
2
3use byteorder::{ByteOrder, LittleEndian};
4use failure::Fail;
5use strum_macros::Display;
6
7use crate::context::Context;
8
9use std::fmt;
10
11/// Assembly uses constant expressions to avoid copying magic numbers around.
12/// Expr represents these constant expressions.
13///
14/// The run method evaluates the constant expression.
15/// The get_2bytes, get_byte and get_bit_index evaluate the constant expression but also convert
16/// to a specific low level type needed by instructions.
17
18#[derive(Clone, PartialEq, Eq, Debug)]
19pub enum Expr {
20    Ident(String),
21    Const(i64),
22    Func(Box<Expr>, Box<Expr>),
23    Binary(Box<BinaryExpr>),
24    Unary(Box<UnaryExpr>),
25}
26
27impl fmt::Display for Expr {
28    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
29        match self {
30            Expr::Ident(ident) => write!(f, "{}", ident),
31            Expr::Const(c) => write!(f, "{}", c),
32            Expr::Func(f_name, f_arg) => write!(f, "{}({})", f_name, f_arg),
33            Expr::Binary(binary) => write!(f, "{}", binary),
34            Expr::Unary(unary) => write!(f, "{}", unary),
35        }
36    }
37}
38
39impl Expr {
40    pub fn binary(left: Expr, operator: BinaryOperator, right: Expr) -> Expr {
41        Expr::Binary(Box::new(BinaryExpr {
42            left,
43            operator,
44            right,
45        }))
46    }
47
48    pub fn unary(expr: Expr, operator: UnaryOperator) -> Expr {
49        Expr::Unary(Box::new(UnaryExpr { expr, operator }))
50    }
51
52    pub fn get_words(&self, constants: &dyn Context) -> Result<[u8; 2], ExprRunError> {
53        let value = self.run(constants)?;
54        if value > 0xFFFF || value < -32768 {
55            Err(ExprRunError::ResultDoesntFit(format!(
56                "0x{} > 0xFFFF This is invalid because the value needs to fit in word",
57                value
58            )))
59        } else {
60            let mut result = [0, 0];
61            LittleEndian::write_u16(&mut result, value as u16);
62            Ok(result)
63        }
64    }
65
66    pub fn get_double_words(&self, constants: &dyn Context) -> Result<[u8; 4], ExprRunError> {
67        let value = self.run(constants)?;
68        if value > std::u32::MAX as i64 || value < std::i32::MIN as i64 {
69            Err(ExprRunError::ResultDoesntFit(format!(
70                "0x{} > 0xFFFF_FFFF This is invalid because the value needs to fit in word",
71                value
72            )))
73        } else {
74            let mut result = [0, 0, 0, 0];
75            LittleEndian::write_u32(&mut result, value as u32);
76            Ok(result)
77        }
78    }
79
80    pub fn get_quad_words(&self, constants: &dyn Context) -> Result<[u8; 8], ExprRunError> {
81        let value = self.run(constants)?;
82
83        let mut result = [0, 0, 0, 0, 0, 0, 0, 0];
84        LittleEndian::write_u64(&mut result, value as u64);
85        Ok(result)
86    }
87
88    pub fn get_byte(&self, constants: &dyn Context) -> Result<u8, ExprRunError> {
89        let value = self.run(constants)?;
90        if value > 0xFF || value < -128 {
91            Err(ExprRunError::ResultDoesntFit(format!(
92                "0x{:x} > 0xFF This is invalid because the value needs to fit in one byte",
93                value
94            )))
95        } else {
96            Ok(value as u8)
97        }
98    }
99
100    pub fn get_bit_index(&self, constants: &dyn Context) -> Result<u8, ExprRunError> {
101        let value = self.run(constants)?;
102        if value > 7 {
103            Err(ExprRunError::ResultDoesntFit(format!(
104                "{} > 7 This is invalid because the value needs to index bits in a byte.",
105                value
106            )))
107        } else {
108            Ok(value as u8)
109        }
110    }
111
112    pub fn run(&self, constants: &dyn Context) -> Result<i64, ExprRunError> {
113        match self {
114            Expr::Ident(ident) => match constants.get_expr(ident) {
115                Some(Expr::Const(address)) => Ok(address),
116                // TODO: check recursion for cross linked equs and other labels
117                Some(expr) => expr.run(constants),
118                None => Err(ExprRunError::MissingIdentifier(ident.clone())),
119            },
120            Expr::Const(value) => Ok(*value),
121            Expr::Func(ident, argument) => {
122                if let Expr::Ident(name) = &**ident {
123                    let value = argument.run(constants)?;
124                    let ret_val = match name.to_lowercase().as_str() {
125                        "low" => (value as u64 & 0xff) as i64,
126                        "high" | "byte2" => ((value as u64 & 0xff00) >> 8) as i64,
127                        "byte3" => ((value as u64 & 0xff0000) >> 16) as i64,
128                        "byte4" => ((value as u64 & 0xff000000) >> 24) as i64,
129                        "lwrd" => (value as u64 & 0xffff) as i64,
130                        "hwrd" => ((value as u64 & 0xffff0000) >> 16) as i64,
131                        "page" => ((value as u64 & 0x1f0000) >> 16) as i64,
132                        "exp2" => 1 << value,
133                        "log2" => {
134                            let mut i = 0;
135                            let mut value = value as u64;
136                            while value > 0 {
137                                value >>= 1;
138                                i += 1
139                            }
140                            i
141                        }
142                        _ => return Err(ExprRunError::MissingFunction(name.clone())),
143                    };
144
145                    Ok(ret_val)
146                } else {
147                    Err(ExprRunError::ResultDoesntFit(format!(
148                        "function name must be Ident, get: {:?}",
149                        ident
150                    )))
151                }
152            }
153            Expr::Binary(binary) => {
154                let left = binary.left.run(constants)?;
155                let right = binary.right.run(constants)?;
156                match binary.operator {
157                    BinaryOperator::Add => match left.checked_add(right) {
158                        Some(value) => Ok(value),
159                        None => Err(ExprRunError::ArithmeticError(format!(
160                            "Addition overflowed: {:?} + {:?}",
161                            binary.left, binary.right
162                        ))),
163                    },
164                    BinaryOperator::Sub => match left.checked_sub(right) {
165                        Some(value) => Ok(value),
166                        None => Err(ExprRunError::ArithmeticError(format!(
167                            "Subtraction underflowed: {:?} - {:?}",
168                            binary.left, binary.right
169                        ))),
170                    },
171                    BinaryOperator::Mul => match left.checked_mul(right) {
172                        Some(value) => Ok(value),
173                        None => Err(ExprRunError::ArithmeticError(format!(
174                            "Multiplication overflowed: {:?} * {:?}",
175                            binary.left, binary.right
176                        ))),
177                    },
178                    BinaryOperator::Div => {
179                        if right == 0 {
180                            Err(ExprRunError::ArithmeticError(format!(
181                                "Attempted to divide by zero: {:?} / {:?}",
182                                binary.left, binary.right
183                            )))
184                        } else {
185                            match left.checked_div(right) {
186                                Some(value) => Ok(value),
187                                None => Err(ExprRunError::ArithmeticError(format!(
188                                    "Division overflowed: {:?} / {:?}",
189                                    binary.left, binary.right
190                                ))),
191                            }
192                        }
193                    }
194                    BinaryOperator::Rem => {
195                        if right == 0 {
196                            Err(ExprRunError::ArithmeticError(format!(
197                                "Attempted to divide by zero (remainder): {:?} % {:?}",
198                                binary.left, binary.right
199                            )))
200                        } else {
201                            match left.checked_rem(right) {
202                                Some(value) => Ok(value),
203                                None => Err(ExprRunError::ArithmeticError(format!(
204                                    "Remainder overflowed: {:?} % {:?}",
205                                    binary.left, binary.right
206                                ))),
207                            }
208                        }
209                    }
210                    BinaryOperator::BitwiseAnd => Ok(left & right),
211                    BinaryOperator::BitwiseOr => Ok(left | right),
212                    BinaryOperator::BitwiseXor => Ok(left ^ right),
213                    BinaryOperator::ShiftLeft => Ok(left << right),
214                    BinaryOperator::ShiftRight => Ok(left >> right),
215                    BinaryOperator::LessThan => Ok((left < right) as i64),
216                    BinaryOperator::LessOrEqual => Ok((left <= right) as i64),
217                    BinaryOperator::GreaterThan => Ok((left > right) as i64),
218                    BinaryOperator::GreaterOrEqual => Ok((left >= right) as i64),
219                    BinaryOperator::Equal => Ok((left == right) as i64),
220                    BinaryOperator::NotEqual => Ok((left < right) as i64),
221                    BinaryOperator::LogicalAnd => Ok((left != 0 && right != 0) as i64),
222                    BinaryOperator::LogicalOr => Ok((left != 0 || right != 0) as i64),
223                }
224            }
225            Expr::Unary(unary) => match unary.operator {
226                UnaryOperator::Minus => {
227                    let value = unary.expr.run(constants)?;
228                    match value.checked_neg() {
229                        Some(value) => Ok(value),
230                        None => Err(ExprRunError::ArithmeticError(format!(
231                            "Failed to get negative value of: {}",
232                            value
233                        ))),
234                    }
235                }
236                UnaryOperator::BitwiseNot => {
237                    let value = unary.expr.run(constants)?;
238                    Ok(value.reverse_bits())
239                }
240                UnaryOperator::LogicalNot => {
241                    let value = unary.expr.run(constants)?;
242                    Ok((value == 0) as i64)
243                }
244            },
245        }
246    }
247}
248
249#[derive(Debug, Fail)]
250pub enum ExprRunError {
251    #[fail(display = "Function {} can not be found.", _0)]
252    MissingFunction(String),
253    #[fail(display = "Identifier {} can not be found.", _0)]
254    MissingIdentifier(String),
255    #[fail(display = "Arithmetic error: {}", _0)]
256    ArithmeticError(String),
257    #[fail(display = "{}", _0)]
258    ResultDoesntFit(String),
259}
260
261#[derive(Clone, PartialEq, Eq, Debug)]
262pub struct BinaryExpr {
263    pub left: Expr,
264    pub operator: BinaryOperator,
265    pub right: Expr,
266}
267
268impl fmt::Display for BinaryExpr {
269    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
270        write!(f, "{}{}{}", self.left, self.operator, self.right)
271    }
272}
273
274#[derive(Clone, PartialEq, Eq, Debug)]
275pub struct UnaryExpr {
276    pub operator: UnaryOperator,
277    pub expr: Expr,
278}
279
280impl fmt::Display for UnaryExpr {
281    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
282        write!(f, "{}{}", self.operator, self.expr)
283    }
284}
285
286#[derive(Clone, PartialEq, Eq, Debug, Display)]
287pub enum BinaryOperator {
288    #[strum(serialize = "+")]
289    Add,
290    #[strum(serialize = "-")]
291    Sub,
292    #[strum(serialize = "*")]
293    Mul,
294    #[strum(serialize = "/")]
295    Div,
296    #[strum(serialize = "%")]
297    Rem,
298    #[strum(serialize = "&")]
299    BitwiseAnd,
300    #[strum(serialize = "^")]
301    BitwiseXor,
302    #[strum(serialize = "|")]
303    BitwiseOr,
304    #[strum(serialize = "<<")]
305    ShiftLeft,
306    #[strum(serialize = ">>")]
307    ShiftRight,
308    #[strum(serialize = "<")]
309    LessThan,
310    #[strum(serialize = "<=")]
311    LessOrEqual,
312    #[strum(serialize = ">")]
313    GreaterThan,
314    #[strum(serialize = ">=")]
315    GreaterOrEqual,
316    #[strum(serialize = "==")]
317    Equal,
318    #[strum(serialize = "!=")]
319    NotEqual,
320    #[strum(serialize = "&&")]
321    LogicalAnd,
322    #[strum(serialize = "||")]
323    LogicalOr,
324}
325
326#[derive(Clone, PartialEq, Eq, Debug, Display)]
327pub enum UnaryOperator {
328    #[strum(serialize = "-")]
329    Minus,
330    #[strum(serialize = "~")]
331    BitwiseNot,
332    #[strum(serialize = "!")]
333    LogicalNot,
334}
335
336#[cfg(test)]
337mod parser_tests {
338    use super::*;
339    use crate::document::document;
340
341    #[test]
342    fn e_ident_test() {
343        assert_eq!(document::e_ident("_"), Ok(Expr::Ident("_".to_string())));
344
345        assert_eq!(document::e_ident("l"), Ok(Expr::Ident("l".to_string())));
346
347        assert_eq!(
348            document::e_ident("Test_string_of_ident"),
349            Ok(Expr::Ident("Test_string_of_ident".to_string()))
350        );
351
352        assert!(document::e_ident("4").is_err());
353    }
354
355    #[test]
356    fn e_const_test() {
357        assert_eq!(document::e_const("4"), Ok(Expr::Const(4)));
358
359        assert_eq!(document::e_const("0x3519"), Ok(Expr::Const(0x3519)));
360
361        assert_eq!(document::e_const("$4a"), Ok(Expr::Const(0x4a)));
362
363        assert_eq!(document::e_const("03516"), Ok(Expr::Const(0o3516)));
364
365        assert_eq!(document::e_const("0b110100"), Ok(Expr::Const(0b110100)));
366
367        assert!(document::e_const("4a").is_err());
368
369        assert!(document::e_const("0x4z").is_err());
370
371        assert!(document::e_const("$4y").is_err());
372
373        assert!(document::e_const("0489").is_err());
374
375        assert!(document::e_const("0b2103").is_err());
376    }
377
378    #[test]
379    fn expr_test() {
380        assert_eq!(
381            document::expr("Test_string_of_ident"),
382            Ok(Expr::Ident("Test_string_of_ident".to_string()))
383        );
384
385        assert_eq!(document::expr("03516"), Ok(Expr::Const(0o3516)));
386
387        assert_eq!(document::expr("' '"), Ok(Expr::Const(0x20)));
388
389        assert_eq!(document::expr("(t)"), Ok(Expr::Ident("t".to_string())));
390
391        assert_eq!(
392            document::expr("high(t)"),
393            Ok(Expr::Func(
394                Box::new(Expr::Ident("high".to_string())),
395                Box::new(Expr::Ident("t".to_string()))
396            ))
397        );
398
399        assert_eq!(
400            document::expr("1 << 2 | 1 << 1"),
401            Ok(Expr::Binary(Box::new(BinaryExpr {
402                left: Expr::Binary(Box::new(BinaryExpr {
403                    left: Expr::Const(1),
404                    operator: BinaryOperator::ShiftLeft,
405                    right: Expr::Const(2),
406                })),
407                operator: BinaryOperator::BitwiseOr,
408                right: Expr::Binary(Box::new(BinaryExpr {
409                    left: Expr::Const(1),
410                    operator: BinaryOperator::ShiftLeft,
411                    right: Expr::Const(1),
412                })),
413            })))
414        );
415    }
416}