Skip to content

Commit

Permalink
feat: recursion
Browse files Browse the repository at this point in the history
  • Loading branch information
viddrobnic committed Jun 8, 2024
1 parent 88eb746 commit df26082
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 3 deletions.
1 change: 1 addition & 0 deletions runtime/src/bytecode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ pub enum Instruction {
StoreLocal(usize),
LoadLocal(usize),
LoadFree(usize),
CurrentClosure,
}

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
Expand Down
8 changes: 6 additions & 2 deletions runtime/src/compiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -466,10 +466,12 @@ impl Compiler {
fn_literal: &ast::FunctionLiteral,
range: Range,
) -> Result<(), Error> {
// TODO: Do something with recursion

self.enter_scope();

if let Some(name) = &fn_literal.name {
self.symbol_table.define_current_closure(name.to_string());
}

// Define argument symbols
for param in &fn_literal.parameters {
self.symbol_table.define(param.to_string());
Expand Down Expand Up @@ -524,6 +526,7 @@ impl Compiler {
Symbol::Global(index) => self.emit(Instruction::StoreGlobal(index), range),
Symbol::Local(index) => self.emit(Instruction::StoreLocal(index), range),
Symbol::Free(_) => panic!("Can't store to free symbol"),
Symbol::CurrentClosure => panic!("Can't store to current closure symbol"),
};
}

Expand All @@ -532,6 +535,7 @@ impl Compiler {
Symbol::Global(index) => self.emit(Instruction::LoadGlobal(index), range),
Symbol::Local(index) => self.emit(Instruction::LoadLocal(index), range),
Symbol::Free(index) => self.emit(Instruction::LoadFree(index), range),
Symbol::CurrentClosure => self.emit(Instruction::CurrentClosure, range),
};
}
}
12 changes: 12 additions & 0 deletions runtime/src/compiler/symbol_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub enum Symbol {
Global(usize),
Local(usize),
Free(usize),
CurrentClosure,
}

#[derive(Debug)]
Expand Down Expand Up @@ -40,6 +41,17 @@ impl SymbolTable {
.expect("Symbol table should have at least one store")
}

pub fn define_current_closure(&mut self, name: String) -> Symbol {
let symbol = Symbol::CurrentClosure;

self.0
.last_mut()
.expect("Symbol table should have at least one store")
.store
.insert(name, symbol);
symbol
}

pub fn define(&mut self, name: String) -> Symbol {
let is_global = self.0.len() == 1;

Expand Down
2 changes: 2 additions & 0 deletions runtime/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ pub enum ErrorKind {
InvalidOrderingType(DataType, DataType),
InvalidEqualityType(DataType, DataType),
InvalidFunctionCalee(DataType),
InvalidNrOfArgs { expected: usize, got: usize },
}

#[derive(Debug, Error, PartialEq)]
Expand Down Expand Up @@ -104,6 +105,7 @@ impl Display for ErrorKind {
),
ErrorKind::ReturnOutsideOfFunction => write!(f, "Return can't be used outside of a function."),
ErrorKind::InvalidFunctionCalee(dt) => write!(f,"Can only call functions, not {dt}"),
ErrorKind::InvalidNrOfArgs { expected, got } =>write!(f, "Invalid number of arguments, expected: {expected}, got: {got}")
}
}
}
9 changes: 8 additions & 1 deletion runtime/src/vm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,10 @@ impl VirtualMachine {
Instruction::StoreLocal(index) => self.store_local(index),
Instruction::LoadLocal(index) => self.load_local(index)?,
Instruction::LoadFree(index) => self.load_free(index)?,
Instruction::CurrentClosure => {
let closure = self.current_frame().closure.clone();
self.push(Object::Closure(closure))?;
}
}

Ok(Some(ip + 1))
Expand Down Expand Up @@ -598,7 +602,10 @@ impl VirtualMachine {

let fun = &functions[closure.function_index];
if fun.nr_arguments != nr_args {
todo!("return error");
return Err(ErrorKind::InvalidNrOfArgs {
expected: fun.nr_arguments,
got: nr_args,
});
}

let nr_local = fun.nr_local_variables;
Expand Down
58 changes: 58 additions & 0 deletions runtime/src/vm/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -515,3 +515,61 @@ fn call_closure() {
run_test(input, Ok(expected));
}
}

#[test]
fn recursion() {
let tests = [
(
r#"
fib = fn(n) {
if (n <= 2) {
return 1
} else {
return fib(n-1) + fib(n-2)
}
}
fib(5)
"#,
Object::Integer(5),
),
(
r#"
wrap = fn() {
fib = fn(n) {
if (n <= 2) {
return 1
} else {
return fib(n-1) + fib(n-2)
}
}
fib(5)
}
wrap()
"#,
Object::Integer(5),
),
(
r#"
outer = fn(do) {
inner = fn() {
outer(false)
}
if (do) {
inner()
} else {
42
}
}
outer(true)
"#,
Object::Integer(42),
),
];

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

0 comments on commit df26082

Please sign in to comment.