Skip to content

Commit

Permalink
feat: compile and execute arrays
Browse files Browse the repository at this point in the history
  • Loading branch information
viddrobnic committed Jun 1, 2024
1 parent 8284942 commit 348de4b
Show file tree
Hide file tree
Showing 8 changed files with 232 additions and 22 deletions.
3 changes: 2 additions & 1 deletion runtime/src/bytecode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ use parser::position::Range;

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Instruction {
Constant(usize),
Pop,
Constant(usize),
Array(usize),
}

#[derive(Debug, PartialEq, Clone)]
Expand Down
27 changes: 18 additions & 9 deletions runtime/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,22 +70,18 @@ impl Compiler {
match &node.value {
ast::NodeValue::Identifier(_) => todo!(),
ast::NodeValue::IntegerLiteral(int) => {
let const_idx = self.add_constant(Object::Integer(*int));
self.emit(Instruction::Constant(const_idx), node.range);
self.compile_constant(Object::Integer(*int), node.range);
}
ast::NodeValue::FloatLiteral(flt) => {
let const_idx = self.add_constant(Object::Float(*flt));
self.emit(Instruction::Constant(const_idx), node.range);
self.compile_constant(Object::Float(*flt), node.range);
}
ast::NodeValue::BoolLiteral(boolean) => {
let const_idx = self.add_constant(Object::Boolean(*boolean));
self.emit(Instruction::Constant(const_idx), node.range);
self.compile_constant(Object::Boolean(*boolean), node.range);
}
ast::NodeValue::StringLiteral(string) => {
let const_idx = self.add_constant(Object::String(Rc::new(string.to_string())));
self.emit(Instruction::Constant(const_idx), node.range);
self.compile_constant(Object::String(Rc::new(string.to_string())), node.range);
}
ast::NodeValue::ArrayLiteral(_) => todo!(),
ast::NodeValue::ArrayLiteral(arr) => self.compile_array(arr, node.range),
ast::NodeValue::HashLiteral(_) => todo!(),
ast::NodeValue::PrefixOperator { .. } => todo!(),
ast::NodeValue::InfixOperator { .. } => todo!(),
Expand All @@ -102,4 +98,17 @@ impl Compiler {
ast::NodeValue::Use(_) => todo!(),
}
}

fn compile_constant(&mut self, constant: Object, range: Range) {
let const_idx = self.add_constant(constant);
self.emit(Instruction::Constant(const_idx), range);
}

fn compile_array(&mut self, arr: &[ast::Node], range: Range) {
for node in arr {
self.compile_node(node);
}

self.emit(Instruction::Array(arr.len()), range);
}
}
12 changes: 7 additions & 5 deletions runtime/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use parser::ast;

pub mod object;

mod bytecode;
Expand All @@ -7,10 +9,10 @@ mod vm;
#[cfg(test)]
mod test;

pub fn run(input: &str) {
// TODO: Haandle errors and add vm
let program = parser::parse(input).unwrap();

pub fn run(program: &ast::Program) {
let compiler = compiler::Compiler::new();
let _bytecode = compiler.compile(&program);
let bytecode = compiler.compile(program);

let vm = vm::VirtualMachine::new();
vm.run(&bytecode);
}
3 changes: 2 additions & 1 deletion runtime/src/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ use std::rc::Rc;

#[derive(Debug, PartialEq, Clone)]
pub enum Object {
Null,
Integer(i64),
Float(f64),
Boolean(bool),
String(Rc<String>),
Null,
Array(Rc<Vec<Object>>),
}
116 changes: 116 additions & 0 deletions runtime/src/test/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,119 @@ fn constants() {
assert_eq!(bytecode, expected);
}
}

#[test]
fn arrays() {
let tests = [
(
"[]",
Bytecode {
constants: vec![],
instructions: vec![Instruction::Array(0), Instruction::Pop],
ranges: vec![
Range {
start: Position::new(0, 0),
end: Position::new(0, 2),
};
2
],
},
),
(
"[1]",
Bytecode {
constants: vec![Object::Integer(1)],
instructions: vec![
Instruction::Constant(0),
Instruction::Array(1),
Instruction::Pop,
],
ranges: vec![
Range {
start: Position::new(0, 1),
end: Position::new(0, 2),
},
Range {
start: Position::new(0, 0),
end: Position::new(0, 3),
},
Range {
start: Position::new(0, 0),
end: Position::new(0, 3),
},
],
},
),
(
"[1, \"foo\"]",
Bytecode {
constants: vec![
Object::Integer(1),
Object::String(Rc::new("foo".to_string())),
],
instructions: vec![
Instruction::Constant(0),
Instruction::Constant(1),
Instruction::Array(2),
Instruction::Pop,
],
ranges: vec![
Range {
start: Position::new(0, 1),
end: Position::new(0, 2),
},
Range {
start: Position::new(0, 4),
end: Position::new(0, 9),
},
Range {
start: Position::new(0, 0),
end: Position::new(0, 10),
},
Range {
start: Position::new(0, 0),
end: Position::new(0, 10),
},
],
},
),
];

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

let tests = [(
"[1, [2, 3], 4]",
Bytecode {
constants: vec![
Object::Integer(1),
Object::Integer(2),
Object::Integer(3),
Object::Integer(4),
],
instructions: vec![
Instruction::Constant(0),
Instruction::Constant(1),
Instruction::Constant(2),
Instruction::Array(2),
Instruction::Constant(3),
Instruction::Array(3),
Instruction::Pop,
],
ranges: vec![],
},
)];

for (input, expected) in tests {
let program = parse(input).unwrap();
let compiler = Compiler::new();
let mut bytecode = compiler.compile(&program);
bytecode.ranges = vec![];

assert_eq!(bytecode, expected);
}
}
1 change: 1 addition & 0 deletions runtime/src/test/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
mod compiler;
mod vm;
56 changes: 56 additions & 0 deletions runtime/src/test/vm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
use std::rc::Rc;

use crate::{compiler::Compiler, object::Object, vm::VirtualMachine};

fn run_test(input: &str, expected: Object) {
let program = parser::parse(input).unwrap();

let compiler = Compiler::new();
let bytecode = compiler.compile(&program);

let vm = VirtualMachine::new();
let obj = vm.run(&bytecode);

assert_eq!(obj, expected);
}

#[test]
fn constants() {
let tests = [
("10", Object::Integer(10)),
("6.9", Object::Float(6.9)),
("\"foo\"", Object::String(Rc::new("foo".to_string()))),
("true", Object::Boolean(true)),
];

for (input, expected) in tests {
run_test(input, expected);
}
}

#[test]
fn array() {
let tests = [
("[]", vec![]),
("[1]", vec![Object::Integer(1)]),
(
"[1, \"foo\"]",
vec![
Object::Integer(1),
Object::String(Rc::new("foo".to_string())),
],
),
(
"[1, [2, 3], 4]",
vec![
Object::Integer(1),
Object::Array(Rc::new(vec![Object::Integer(2), Object::Integer(3)])),
Object::Integer(4),
],
),
];

for (input, expected) in tests {
run_test(input, Object::Array(Rc::new(expected)));
}
}
36 changes: 30 additions & 6 deletions runtime/src/vm.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
use crate::{bytecode::Bytecode, object::Object};
use std::rc::Rc;

use crate::{
bytecode::{Bytecode, Instruction},
object::Object,
};

const STACK_SIZE: usize = 4096;

Expand Down Expand Up @@ -28,20 +33,39 @@ impl VirtualMachine {
}

fn pop(&mut self) -> Object {
if self.sp == 0 {
panic!("No more elements");
}

self.sp -= 1;
self.stack[self.sp].clone()
}

pub fn run(&mut self, bytecode: &Bytecode) {
/// Runs the program and returns the first element on the stack.
///
/// This is primarily used to test if the vm works correctly.
/// The first element on the stack should be the last popped element
/// if the compiler and vm both work correctly.
pub fn run(mut self, bytecode: &Bytecode) -> Object {
for inst in &bytecode.instructions {
match inst {
crate::bytecode::Instruction::Constant(idx) => {
self.push(bytecode.constants[*idx].clone())
}
crate::bytecode::Instruction::Pop => {
Instruction::Constant(idx) => self.push(bytecode.constants[*idx].clone()),
Instruction::Pop => {
self.pop();
}
Instruction::Array(len) => self.execute_array(*len),
}
}

self.stack[0].clone()
}

fn execute_array(&mut self, len: usize) {
let start = self.sp - len;

let arr = self.stack[start..self.sp].to_vec();
self.sp -= len;

self.push(Object::Array(Rc::new(arr)));
}
}

0 comments on commit 348de4b

Please sign in to comment.