1
0

Implement functions calls

This commit is contained in:
Jeff 2024-10-19 17:24:22 -04:00
parent 65e513488f
commit 9d5c9d9fd0
6 changed files with 94 additions and 38 deletions

View File

@ -208,11 +208,12 @@ impl Instruction {
instruction instruction
} }
pub fn call(to_register: u8, function_index: u8) -> Instruction { pub fn call(to_register: u8, function_register: u8, argument_count: u8) -> Instruction {
let mut instruction = Instruction(Operation::Call as u32); let mut instruction = Instruction(Operation::Call as u32);
instruction.set_a(to_register); instruction.set_a(to_register);
instruction.set_b(function_index); instruction.set_b(function_register);
instruction.set_c(argument_count);
instruction instruction
} }
@ -557,11 +558,19 @@ impl Instruction {
} }
Operation::Call => { Operation::Call => {
let to_register = self.a(); let to_register = self.a();
let function_index = self.b(); let function_register = self.b();
let argument_count = self.c();
let mut output = format!("R{function_index}("); let mut output = format!("R{to_register} = R{function_register}(");
let first_argument = function_register + 1;
for (index, register) in
(first_argument..first_argument + argument_count).enumerate()
{
if index > 0 {
output.push_str(", ");
}
for register in function_index + 1..to_register {
output.push_str(&format!("R{}", register)); output.push_str(&format!("R{}", register));
} }
@ -778,11 +787,12 @@ mod tests {
#[test] #[test]
fn call() { fn call() {
let instruction = Instruction::call(4, 3); let instruction = Instruction::call(1, 3, 4);
assert_eq!(instruction.operation(), Operation::Call); assert_eq!(instruction.operation(), Operation::Call);
assert_eq!(instruction.a(), 4); assert_eq!(instruction.a(), 1);
assert_eq!(instruction.b(), 3); assert_eq!(instruction.b(), 3);
assert_eq!(instruction.c(), 4);
} }
#[test] #[test]

View File

@ -7,7 +7,7 @@ const LOAD_BOOLEAN: u8 = 0b0000_0010;
const LOAD_CONSTANT: u8 = 0b0000_0011; const LOAD_CONSTANT: u8 = 0b0000_0011;
const LOAD_LIST: u8 = 0b0000_0100; const LOAD_LIST: u8 = 0b0000_0100;
const DECLARE_LOCAL: u8 = 0b0000_0101; const DEFINE_LOCAL: u8 = 0b0000_0101;
const GET_LOCAL: u8 = 0b0000_0110; const GET_LOCAL: u8 = 0b0000_0110;
const SET_LOCAL: u8 = 0b0000_0111; const SET_LOCAL: u8 = 0b0000_0111;
@ -43,22 +43,22 @@ pub enum Operation {
LoadList = LOAD_LIST as isize, LoadList = LOAD_LIST as isize,
// Variables // Variables
DefineLocal = DECLARE_LOCAL as isize, DefineLocal = DEFINE_LOCAL as isize,
GetLocal = GET_LOCAL as isize, GetLocal = GET_LOCAL as isize,
SetLocal = SET_LOCAL as isize, SetLocal = SET_LOCAL as isize,
// Binary operations // Binary math
Add = ADD as isize, Add = ADD as isize,
Subtract = SUBTRACT as isize, Subtract = SUBTRACT as isize,
Multiply = MULTIPLY as isize, Multiply = MULTIPLY as isize,
Divide = DIVIDE as isize, Divide = DIVIDE as isize,
Modulo = MODULO as isize, Modulo = MODULO as isize,
// Logical operations // Binary logic
Test = TEST as isize, Test = TEST as isize,
TestSet = TEST_SET as isize, TestSet = TEST_SET as isize,
// Relational operations // Binary comparison
Equal = EQUAL as isize, Equal = EQUAL as isize,
Less = LESS as isize, Less = LESS as isize,
LessEqual = LESS_EQUAL as isize, LessEqual = LESS_EQUAL as isize,
@ -105,7 +105,7 @@ impl From<u8> for Operation {
LOAD_BOOLEAN => Operation::LoadBoolean, LOAD_BOOLEAN => Operation::LoadBoolean,
LOAD_CONSTANT => Operation::LoadConstant, LOAD_CONSTANT => Operation::LoadConstant,
LOAD_LIST => Operation::LoadList, LOAD_LIST => Operation::LoadList,
DECLARE_LOCAL => Operation::DefineLocal, DEFINE_LOCAL => Operation::DefineLocal,
GET_LOCAL => Operation::GetLocal, GET_LOCAL => Operation::GetLocal,
SET_LOCAL => Operation::SetLocal, SET_LOCAL => Operation::SetLocal,
ADD => Operation::Add, ADD => Operation::Add,
@ -142,7 +142,7 @@ impl From<Operation> for u8 {
Operation::LoadBoolean => LOAD_BOOLEAN, Operation::LoadBoolean => LOAD_BOOLEAN,
Operation::LoadConstant => LOAD_CONSTANT, Operation::LoadConstant => LOAD_CONSTANT,
Operation::LoadList => LOAD_LIST, Operation::LoadList => LOAD_LIST,
Operation::DefineLocal => DECLARE_LOCAL, Operation::DefineLocal => DEFINE_LOCAL,
Operation::GetLocal => GET_LOCAL, Operation::GetLocal => GET_LOCAL,
Operation::SetLocal => SET_LOCAL, Operation::SetLocal => SET_LOCAL,
Operation::Add => ADD, Operation::Add => ADD,

View File

@ -183,21 +183,6 @@ impl<'src> Parser<'src> {
let constant_index = self.chunk.push_constant(value, position)?; let constant_index = self.chunk.push_constant(value, position)?;
let register = self.next_register(); let register = self.next_register();
if let Some(instruction) = self
.current_statement
.last()
.map(|(instruction, _)| instruction)
{
if let Operation::LoadConstant = instruction.operation() {
self.push_instruction(
Instruction::load_constant(instruction.a(), constant_index, false),
position,
);
return Ok(());
}
}
self.push_instruction( self.push_instruction(
Instruction::load_constant(register, constant_index, false), Instruction::load_constant(register, constant_index, false),
position, position,
@ -1201,21 +1186,21 @@ impl<'src> Parser<'src> {
} }
let start = self.current_position.0; let start = self.current_position.0;
let function_register = last_instruction.a();
self.advance()?; self.advance()?;
let function_register = self.next_register();
while !self.allow(Token::RightParenthesis)? { while !self.allow(Token::RightParenthesis)? {
self.parse_expression()?; self.parse_expression()?;
self.allow(Token::Comma)?; self.allow(Token::Comma)?;
} }
let end = self.current_position.1; let end = self.current_position.1;
let register = self.next_register(); let argument_count = self.next_register() - function_register - 1;
let to_register = self.next_register();
self.push_instruction( self.push_instruction(
Instruction::call(register, function_register), Instruction::call(to_register, function_register, argument_count),
Span(start, end), Span(start, end),
); );

View File

@ -347,8 +347,9 @@ impl Vm {
} }
Operation::Call => { Operation::Call => {
let to_register = instruction.a(); let to_register = instruction.a();
let function_index = instruction.b(); let function_register = instruction.b();
let value = self.get(function_index, position)?.clone(); let argument_count = instruction.c();
let value = self.get(function_register, position)?.clone();
let function = if let Value::Function(function) = value { let function = if let Value::Function(function) = value {
function function
} else { } else {
@ -358,8 +359,11 @@ impl Vm {
}); });
}; };
let mut function_vm = Vm::new(function.take_chunk()); let mut function_vm = Vm::new(function.take_chunk());
let first_argument_index = function_register + 1;
for argument_index in function_index + 1..to_register { for argument_index in
first_argument_index..first_argument_index + argument_count
{
let argument = self.get(argument_index, position)?.clone(); let argument = self.get(argument_index, position)?.clone();
let top_of_stack = function_vm.stack.len() as u8; let top_of_stack = function_vm.stack.len() as u8;
@ -369,7 +373,7 @@ impl Vm {
let return_value = function_vm.run()?; let return_value = function_vm.run()?;
if let Some(value) = return_value { if let Some(value) = return_value {
self.set(function_index, value, position)?; self.set(to_register, value, position)?;
} }
} }
Operation::Return => { Operation::Return => {

View File

@ -346,6 +346,52 @@ fn function() {
); );
} }
#[test]
fn function_call() {
let source = "fn(a: int, b: int) -> int { a + b }(1, 2)";
assert_eq!(
parse(source),
Ok(Chunk::with_data(
vec![
(Instruction::load_constant(0, 0, false), Span(0, 36)),
(Instruction::load_constant(1, 1, false), Span(36, 37)),
(Instruction::load_constant(2, 2, false), Span(39, 40)),
(Instruction::call(3, 0, 2), Span(35, 41)),
(Instruction::r#return(true), Span(41, 41)),
],
vec![
Value::function(
Chunk::with_data(
vec![
(Instruction::add(2, 0, 1), Span(30, 31)),
(Instruction::r#return(true), Span(34, 35)),
],
vec![],
vec![
Local::new(Identifier::new("a"), Some(Type::Integer), false, 0, 0),
Local::new(Identifier::new("b"), Some(Type::Integer), false, 0, 1)
]
),
FunctionType {
type_parameters: None,
value_parameters: Some(vec![
(Identifier::new("a"), Type::Integer),
(Identifier::new("b"), Type::Integer)
]),
return_type: Some(Box::new(Type::Integer)),
}
),
Value::integer(1),
Value::integer(2)
],
vec![]
)),
);
assert_eq!(run(source), Ok(Some(Value::integer(3))));
}
#[test] #[test]
fn greater() { fn greater() {
let source = "1 > 2"; let source = "1 > 2";
@ -416,7 +462,7 @@ fn if_else_expression() {
), ),
(Instruction::jump(1, true), Span(5, 7)), (Instruction::jump(1, true), Span(5, 7)),
(Instruction::load_constant(0, 2, true), Span(12, 13)), (Instruction::load_constant(0, 2, true), Span(12, 13)),
(Instruction::load_constant(0, 3, false), Span(23, 24)), (Instruction::load_constant(1, 3, false), Span(23, 24)),
(Instruction::r#return(true), Span(26, 26)), (Instruction::r#return(true), Span(26, 26)),
], ],
vec![ vec![

11
examples/fibonacci.ds Normal file
View File

@ -0,0 +1,11 @@
let fib = fn (n: int) -> int {
if n == 0 {
return 0;
} else if n == 1 {
return 1;
} else {
return fib(n - 1) + fib(n - 2);
}
};
fib(10)