Skip to content

Commit

Permalink
feat: execute prefix operators
Browse files Browse the repository at this point in the history
  • Loading branch information
viddrobnic committed Jun 2, 2024
1 parent bdf8719 commit b6c8946
Show file tree
Hide file tree
Showing 6 changed files with 180 additions and 2 deletions.
3 changes: 3 additions & 0 deletions runtime/src/bytecode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ pub enum Instruction {
Constant(usize),
Array(usize),
HashMap(usize),

Minus,
Bang,
}

#[derive(Debug, PartialEq, Clone)]
Expand Down
20 changes: 18 additions & 2 deletions runtime/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ use crate::{
object::Object,
};

use parser::{ast, position::Range};
use parser::{
ast::{self, PrefixOperatorKind},
position::Range,
};

#[derive(Debug, Default)]
struct Scope {
Expand Down Expand Up @@ -83,7 +86,7 @@ impl Compiler {
}
ast::NodeValue::ArrayLiteral(arr) => self.compile_array(arr, node.range),
ast::NodeValue::HashLiteral(elements) => self.compile_hash_map(elements, node.range),
ast::NodeValue::PrefixOperator { .. } => todo!(),
ast::NodeValue::PrefixOperator { .. } => self.compile_prefix_operator(node),
ast::NodeValue::InfixOperator { .. } => todo!(),
ast::NodeValue::Assign { .. } => todo!(),
ast::NodeValue::Index { .. } => todo!(),
Expand Down Expand Up @@ -120,4 +123,17 @@ impl Compiler {

self.emit(Instruction::HashMap(elements.len() * 2), range);
}

fn compile_prefix_operator(&mut self, node: &ast::Node) {
let ast::NodeValue::PrefixOperator { operator, right } = &node.value else {
panic!("Expected prefix operator node, got: {node:?}");
};

self.compile_node(right);

match operator {
PrefixOperatorKind::Not => self.emit(Instruction::Bang, node.range),
PrefixOperatorKind::Negative => self.emit(Instruction::Minus, node.range),
};
}
}
2 changes: 2 additions & 0 deletions runtime/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::object::DataType;
pub enum ErrorKind {
StackOverflow,
NotHashable(DataType),
InvalidNegateOperand(DataType),
}

#[derive(Debug, Error, PartialEq)]
Expand All @@ -28,6 +29,7 @@ impl Display for ErrorKind {
match self {
ErrorKind::StackOverflow => write!(f, "Stack overflow"),
ErrorKind::NotHashable(data_type) => write!(f, "Data type {data_type} can't be hashed"),
ErrorKind::InvalidNegateOperand(dt) => write!(f, "Can not negate {dt}"),
}
}
}
114 changes: 114 additions & 0 deletions runtime/src/test/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,3 +285,117 @@ fn hash_map() {
assert_eq!(bytecode, expected);
}
}

#[test]
fn prefix_operator() {
let tests = [
(
"-10",
Bytecode {
constants: vec![Object::Integer(10)],
instructions: vec![
Instruction::Constant(0),
Instruction::Minus,
Instruction::Pop,
],
ranges: vec![
Range {
start: Position::new(0, 1),
end: Position::new(0, 3),
},
Range {
start: Position::new(0, 0),
end: Position::new(0, 3),
},
Range {
start: Position::new(0, 0),
end: Position::new(0, 3),
},
],
},
),
(
"-4.2",
Bytecode {
constants: vec![Object::Float(4.2)],
instructions: vec![
Instruction::Constant(0),
Instruction::Minus,
Instruction::Pop,
],
ranges: vec![
Range {
start: Position::new(0, 1),
end: Position::new(0, 4),
},
Range {
start: Position::new(0, 0),
end: Position::new(0, 4),
},
Range {
start: Position::new(0, 0),
end: Position::new(0, 4),
},
],
},
),
(
"!10",
Bytecode {
constants: vec![Object::Integer(10)],
instructions: vec![
Instruction::Constant(0),
Instruction::Bang,
Instruction::Pop,
],
ranges: vec![
Range {
start: Position::new(0, 1),
end: Position::new(0, 3),
},
Range {
start: Position::new(0, 0),
end: Position::new(0, 3),
},
Range {
start: Position::new(0, 0),
end: Position::new(0, 3),
},
],
},
),
(
"!false",
Bytecode {
constants: vec![Object::Boolean(false)],
instructions: vec![
Instruction::Constant(0),
Instruction::Bang,
Instruction::Pop,
],
ranges: vec![
Range {
start: Position::new(0, 1),
end: Position::new(0, 6),
},
Range {
start: Position::new(0, 0),
end: Position::new(0, 6),
},
Range {
start: Position::new(0, 0),
end: Position::new(0, 6),
},
],
},
),
];

for (input, expected) in tests {
let program = parse(input).unwrap();
let compiler = Compiler::new();
let bytecode = compiler.compile(&program);

assert_eq!(bytecode, expected);
}
}
17 changes: 17 additions & 0 deletions runtime/src/test/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,20 @@ fn hash_map_error() {
run_test(input, Err(expected));
}
}

#[test]
fn prefix_operator() {
let tests = [
("-10", Object::Integer(-10)),
("-4.2", Object::Float(-4.2)),
("--10", Object::Integer(10)),
("-(-10)", Object::Integer(10)),
("!false", Object::Boolean(true)),
("!!false", Object::Boolean(false)),
("!42", Object::Integer(-43)), // two's complement
];

for (input, expected) in tests {
run_test(input, Ok(expected));
}
}
26 changes: 26 additions & 0 deletions runtime/src/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ impl VirtualMachine {
}
Instruction::Array(len) => self.execute_array(len)?,
Instruction::HashMap(len) => self.execute_hash_map(len)?,
Instruction::Minus => self.execute_minus()?,
Instruction::Bang => self.execute_bang()?,
}

Ok(())
Expand Down Expand Up @@ -101,4 +103,28 @@ impl VirtualMachine {
self.sp -= len;
self.push(Object::HashMap(Rc::new(hash_map?)))
}

fn execute_minus(&mut self) -> Result<(), ErrorKind> {
let value = self.pop();
match value {
Object::Integer(int) => self.push(Object::Integer(-int))?,
Object::Float(float) => self.push(Object::Float(-float))?,

_ => return Err(ErrorKind::InvalidNegateOperand(value.into())),
};

Ok(())
}

fn execute_bang(&mut self) -> Result<(), ErrorKind> {
let value = self.pop();
match value {
Object::Boolean(boolean) => self.push(Object::Boolean(!boolean))?,
Object::Integer(int) => self.push(Object::Integer(!int))?,

_ => return Err(ErrorKind::InvalidNegateOperand(value.into())),
};

Ok(())
}
}

0 comments on commit b6c8946

Please sign in to comment.