use crate::ast::ast::ASTNode;
use crate::bytecode::bytecode_interpreter::Opcode;
use crate::bytecode::bytecode_interpreter::Opcode::{ADD, DIVIDE, MULTIPLY, PUSH, SUBTRACT, EXP};
pub fn generate(ast: &ASTNode) -> Vec<u8> {
let mut code = Vec::new();
let mut generator_fn = | token: &ASTNode| {
match token {
ASTNode::Number { value } => {
code.push(Opcode::byte_from_opcode(&PUSH));
code.extend_from_slice(&f64::to_le_bytes(*value));
},
ASTNode::Add { .. } => code.push(Opcode::byte_from_opcode(&ADD)),
ASTNode::Subtract { .. } => code.push(Opcode::byte_from_opcode(&SUBTRACT)),
ASTNode::Multiply { .. } => code.push(Opcode::byte_from_opcode(&MULTIPLY)),
ASTNode::Divide { .. } => code.push(Opcode::byte_from_opcode(&DIVIDE)),
ASTNode::Exponentiate { .. } => code.push(Opcode::byte_from_opcode(&EXP)),
}
};
ast.postorder_traverse(&mut generator_fn);
code
}
#[cfg(test)]
mod tests {
use crate::ast::ast::ASTNode;
use crate::bytecode::bytecode_generator::generate;
use crate::bytecode::bytecode_interpreter::interpret;
#[test]
fn test_add() {
let left = Box::new(ASTNode::Number { value: 1.0 });
let right = Box::new(ASTNode::Number { value: -2.0 });
let add = ASTNode::Add { left, right };
let bytecode = generate(&add);
assert_eq!(-1.0, interpret(&bytecode).unwrap());
}
#[test]
fn test_subtract() {
let left = Box::new(ASTNode::Number { value: 1.0 });
let right = Box::new(ASTNode::Number { value: 2.0 });
let subtract = ASTNode::Subtract { left, right };
let bytecode = generate(&subtract);
assert_eq!(-1.0, interpret(&bytecode).unwrap());
}
#[test]
fn test_multiply() {
let left = Box::new(ASTNode::Number { value: 2.0 });
let right = Box::new(ASTNode::Number { value: -2.0 });
let multiply = ASTNode::Multiply { left, right };
let bytecode = generate(&multiply);
assert_eq!(-4.0, interpret(&bytecode).unwrap());
}
#[test]
fn test_divide() {
let left = Box::new(ASTNode::Number { value: -2.0 });
let right = Box::new(ASTNode::Number { value: 4.0 });
let div = ASTNode::Divide { left, right };
let bytecode = generate(&div);
assert_eq!(-0.5, interpret(&bytecode).unwrap());
}
#[test]
fn test_divide_zero() {
let left = Box::new(ASTNode::Number { value: 1.0 });
let right = Box::new(ASTNode::Number { value: 0.0 });
let div = ASTNode::Divide { left, right };
let bytecode = generate(&div);
assert_eq!(Err("Cannot divide by zero.\nNo result.\n".to_string()), interpret(&bytecode));
}
}