Skip to content

Commit

Permalink
Merge pull request #19 from ZakFarmer/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
ZakFarmer authored Oct 29, 2023
2 parents a94ab8f + 9dd8357 commit be48071
Show file tree
Hide file tree
Showing 8 changed files with 200 additions and 67 deletions.
2 changes: 0 additions & 2 deletions .github/workflows/deploy-wasm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ name: Deploy WASM bundle
on:
push:
branches: ["main"]
pull_request:
branches: ["main"]

jobs:
build:
Expand Down
35 changes: 19 additions & 16 deletions compiler/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::rc::Rc;

use anyhow::Error;
use lexer::token::TokenType;
use lexer::token::{Token, TokenType};
use opcode::{Instructions, Opcode};
use parser::ast::{
BlockStatement, BooleanLiteral, Expression, IntegerLiteral, Literal, Node, Statement,
Expand Down Expand Up @@ -257,10 +257,10 @@ impl Compiler {
&mut self,
left: &Box<Expression>,
right: &Box<Expression>,
operator: &str,
operator: Token,
) -> Result<(), Error> {
match operator {
"<" => {
match operator.token_type {
TokenType::Lt => {
self.compile_expression(right)?;
self.compile_expression(left)?;
}
Expand Down Expand Up @@ -300,6 +300,10 @@ impl Compiler {
}
Expression::Function(function_literal) => {
self.enter_scope();

for parameter in function_literal.parameters.iter() {
self.symbol_table.define(&parameter.value);
}

self.compile_block_statement(&function_literal.body)?;

Expand All @@ -311,9 +315,10 @@ impl Compiler {
self.emit(Opcode::OpReturn, vec![]);
}

let num_locals = self.symbol_table.num_definitions;
let instructions = self.exit_scope();

let compiled_function = Rc::from(object::CompiledFunction::new(instructions));
let compiled_function = Rc::from(object::CompiledFunction::new(instructions, num_locals));

let operands =
vec![self.add_constant(object::Object::CompiledFunction(compiled_function))];
Expand All @@ -325,6 +330,10 @@ impl Compiler {
Expression::Call(call_expression) => {
self.compile_expression(&call_expression.function)?;

for argument in call_expression.arguments.iter() {
self.compile_expression(argument)?;
}

self.emit(Opcode::OpCall, vec![call_expression.arguments.len()]);

Ok(())
Expand Down Expand Up @@ -369,17 +378,11 @@ impl Compiler {
Ok(())
}
Expression::Infix(infix_expression) => {
if infix_expression.operator.token_type == TokenType::Lt {
self.compile_expression(&infix_expression.right)?;
self.compile_expression(&infix_expression.left)?;

self.emit(opcode::Opcode::OpGreaterThan, vec![]);

return Ok(());
}

self.compile_expression(&infix_expression.left)?;
self.compile_expression(&infix_expression.right)?;
self.compile_operands(
&infix_expression.left,
&infix_expression.right,
infix_expression.operator.clone(),
)?;

match infix_expression.operator.token_type {
TokenType::Plus => self.emit(opcode::Opcode::OpAdd, vec![]),
Expand Down
85 changes: 57 additions & 28 deletions compiler/tests/compiler_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,14 +252,21 @@ fn test_functions() -> Result<(), Error> {
expected_constants: vec![
Object::Integer(5),
Object::Integer(10),
Object::CompiledFunction(Rc::new(object::CompiledFunction::new(concat_instructions(
&vec![
opcode::make(opcode::Opcode::OpConst, &vec![0]),
opcode::make(opcode::Opcode::OpConst, &vec![1]),
opcode::make(opcode::Opcode::OpAdd, &vec![]),
opcode::make(opcode::Opcode::OpReturnValue, &vec![]),
],
)))),
Object::CompiledFunction(
Rc::new(
object::CompiledFunction::new(
concat_instructions(
&vec![
opcode::make(opcode::Opcode::OpConst, &vec![0]),
opcode::make(opcode::Opcode::OpConst, &vec![1]),
opcode::make(opcode::Opcode::OpAdd, &vec![]),
opcode::make(opcode::Opcode::OpReturnValue, &vec![]),
],
),
0
)
)
),
],
expected_instructions: vec![
opcode::make(opcode::Opcode::OpConst, &vec![2]),
Expand All @@ -280,7 +287,7 @@ fn test_functions_with_no_return_value() -> Result<(), Error> {
object::CompiledFunction::new(concat_instructions(&vec![opcode::make(
opcode::Opcode::OpReturn,
&vec![],
)])),
)]), 0),
))],
expected_instructions: vec![
opcode::make(opcode::Opcode::OpConst, &vec![0]),
Expand All @@ -295,25 +302,47 @@ fn test_functions_with_no_return_value() -> Result<(), Error> {

#[test]
fn test_function_calls() -> Result<(), Error> {
let tests = vec![CompilerTestCase {
input: "$noArg = function () { return 24; }; $noArg();".to_string(),
expected_constants: vec![
Object::Integer(24),
Object::CompiledFunction(Rc::new(object::CompiledFunction::new(concat_instructions(
&vec![
opcode::make(opcode::Opcode::OpConst, &vec![0]),
opcode::make(opcode::Opcode::OpReturnValue, &vec![]),
],
)))),
],
expected_instructions: vec![
opcode::make(opcode::Opcode::OpConst, &vec![1]),
opcode::make(opcode::Opcode::OpSetGlobal, &vec![0]),
opcode::make(opcode::Opcode::OpGetGlobal, &vec![0]),
opcode::make(opcode::Opcode::OpCall, &vec![0]),
opcode::make(opcode::Opcode::OpPop, &vec![]),
],
}];
let tests = vec![
CompilerTestCase {
input: "$noArg = function () { return 24; }; $noArg();".to_string(),
expected_constants: vec![
Object::Integer(24),
Object::CompiledFunction(Rc::new(object::CompiledFunction::new(concat_instructions(
&vec![
opcode::make(opcode::Opcode::OpConst, &vec![0]),
opcode::make(opcode::Opcode::OpReturnValue, &vec![]),
],
), 0))),
],
expected_instructions: vec![
opcode::make(opcode::Opcode::OpConst, &vec![1]),
opcode::make(opcode::Opcode::OpSetGlobal, &vec![0]),
opcode::make(opcode::Opcode::OpGetGlobal, &vec![0]),
opcode::make(opcode::Opcode::OpCall, &vec![0]),
opcode::make(opcode::Opcode::OpPop, &vec![]),
],
},
CompilerTestCase {
input: "$oneArg = function ($a) { return $a; }; $oneArg(24);".to_string(),
expected_constants: vec![
Object::CompiledFunction(Rc::new(object::CompiledFunction::new(concat_instructions(
&vec![
opcode::make(opcode::Opcode::OpGetLocal, &vec![0]),
opcode::make(opcode::Opcode::OpReturnValue, &vec![]),
],
), 1))),
Object::Integer(24),
],
expected_instructions: vec![
opcode::make(opcode::Opcode::OpConst, &vec![0]),
opcode::make(opcode::Opcode::OpSetGlobal, &vec![0]),
opcode::make(opcode::Opcode::OpGetGlobal, &vec![0]),
opcode::make(opcode::Opcode::OpConst, &vec![1]),
opcode::make(opcode::Opcode::OpCall, &vec![1]),
opcode::make(opcode::Opcode::OpPop, &vec![]),
],
},
];

run_compiler_tests(tests)?;

Expand Down
52 changes: 48 additions & 4 deletions compiler/tests/symbol_table_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,32 @@ fn test_define() -> Result<(), Error> {
"c".to_string(),
Symbol {
name: "c".to_string(),
scope: SymbolScope::Global,
index: 2,
scope: SymbolScope::Local,
index: 0,
},
),
(
"d".to_string(),
Symbol {
name: "d".to_string(),
scope: SymbolScope::Global,
index: 3,
scope: SymbolScope::Local,
index: 1,
},
),
(
"e".to_string(),
Symbol {
name: "e".to_string(),
scope: SymbolScope::Local,
index: 0,
},
),
(
"f".to_string(),
Symbol {
name: "f".to_string(),
scope: SymbolScope::Local,
index: 1,
},
),
]);
Expand All @@ -54,6 +70,34 @@ fn test_define() -> Result<(), Error> {
panic!("b.name not 'b'. got={}", b.name);
}

let mut first_local = SymbolTable::new_enclosed(global.clone());

let c = first_local.define("c");

if c.name != "c" {
panic!("c.name not 'c'. got={}", c.name);
}

let d = first_local.define("d");

if d.name != "d" {
panic!("d.name not 'd'. got={}", d.name);
}

let mut second_local = SymbolTable::new_enclosed(first_local.clone());

let e = second_local.define("e");

if e.name != "e" {
panic!("e.name not 'e'. got={}", e.name);
}

let f = second_local.define("f");

if f.name != "f" {
panic!("f.name not 'f'. got={}", f.name);
}

Ok(())
}

Expand Down
10 changes: 7 additions & 3 deletions object/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ impl std::fmt::Display for Object {

write!(f, "fn({}) {{\n{}\n}}", parameters_string, body)
}
Object::CompiledFunction(function) => write!(f, "{}", function),
Object::Array(elements) => {
let mut elements_string = String::new();

Expand All @@ -54,18 +53,23 @@ impl std::fmt::Display for Object {
}
Object::Return(value) => write!(f, "{}", value),
Object::Null => write!(f, "null"),
_ => Ok(()),
}
}
}

#[derive(Clone, Debug, PartialEq)]
pub struct CompiledFunction {
pub instructions: Instructions,
pub num_locals: usize,
}

impl CompiledFunction {
pub fn new(instructions: Instructions) -> Self {
Self { instructions }
pub fn new(instructions: Instructions, num_locals: usize) -> Self {
Self {
instructions,
num_locals,
}
}

pub fn instructions(&self) -> &Instructions {
Expand Down
44 changes: 31 additions & 13 deletions vm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,36 @@ impl Vm {
&self.globals
}

fn call_function(&mut self, num_args: usize) {
let function = &*self.stack[self.stack_pointer - 1 - num_args];

match function {
Object::CompiledFunction(compiled_function) => {
let base_pointer = self.stack_pointer - num_args;
let cloned_function = compiled_function.as_ref().clone();

let frame = frame::Frame::new(
cloned_function,
base_pointer,
);

self.stack_pointer = base_pointer + compiled_function.num_locals as usize;
self.push_frame(frame);
}
_ => {
panic!("calling non-function object: {}", function);
}
}
}

pub fn new(bytecode: Bytecode) -> Self {
let empty_frame = frame::Frame::new(CompiledFunction::new(Instructions(vec![])), 0);
let empty_frame = frame::Frame::new(CompiledFunction::new(Instructions(vec![]), 0), 0);

let main_function = CompiledFunction::new(
bytecode.instructions.clone(),
0
);

let main_function = CompiledFunction::new(bytecode.instructions.clone());
let main_frame = frame::Frame::new(main_function, 0);

let mut frames = vec![empty_frame; MAX_FRAMES];
Expand Down Expand Up @@ -157,19 +183,11 @@ impl Vm {
self.stack[base_pointer + local_index] = self.pop();
}
Opcode::OpCall => {
self.current_frame().instruction_pointer += 1;
let num_args = instructions[instruction_pointer + 1] as usize;

let function = &*self.stack[self.stack_pointer - 1];

if let Object::CompiledFunction(function) = function {
let frame =
frame::Frame::new(function.as_ref().clone(), self.stack_pointer);
self.current_frame().instruction_pointer += 1;

self.stack_pointer = frame.base_pointer;
self.push_frame(frame);
} else {
return Err(Error::msg(format!("calling non-function: {}", function)));
}
self.call_function(num_args);
}
Opcode::OpReturn => {
let frame = self.pop_frame();
Expand Down
26 changes: 26 additions & 0 deletions vm/tests/vm_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,32 @@ fn test_functions_with_no_arguments() -> Result<(), Error> {
Ok(())
}

#[test]
fn test_functions_with_arguments() -> Result<(), Error> {
let tests = vec![
VmTestCase {
input: "$identity = function ($x) { $x; }; $identity(4);".to_string(),
expected: Object::Integer(4),
},
VmTestCase {
input: "$identity = function ($x) { return $x; }; $identity(4);".to_string(),
expected: Object::Integer(4),
},
VmTestCase {
input: "$double = function ($x) { $x * 2; }; $double(4);".to_string(),
expected: Object::Integer(8),
},
VmTestCase {
input: "$add = function ($x, $y) { $x + $y; }; $add(4, 5);".to_string(),
expected: Object::Integer(9),
},
];

run_vm_tests(tests)?;

Ok(())
}

#[test]
fn test_functions_with_no_return_value() -> Result<(), Error> {
let tests = vec![VmTestCase {
Expand Down
Loading

0 comments on commit be48071

Please sign in to comment.