From 9d5c9d9fd0d53127547e276f94b7489cc5aa521c Mon Sep 17 00:00:00 2001 From: Jeff Date: Sat, 19 Oct 2024 17:24:22 -0400 Subject: [PATCH] Implement functions calls --- dust-lang/src/instruction.rs | 24 ++++++++++++----- dust-lang/src/operation.rs | 14 +++++----- dust-lang/src/parser.rs | 23 +++------------- dust-lang/src/vm.rs | 12 ++++++--- dust-lang/tests/expressions.rs | 48 +++++++++++++++++++++++++++++++++- examples/fibonacci.ds | 11 ++++++++ 6 files changed, 94 insertions(+), 38 deletions(-) create mode 100644 examples/fibonacci.ds diff --git a/dust-lang/src/instruction.rs b/dust-lang/src/instruction.rs index 0935973..04e5be3 100644 --- a/dust-lang/src/instruction.rs +++ b/dust-lang/src/instruction.rs @@ -208,11 +208,12 @@ impl 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); instruction.set_a(to_register); - instruction.set_b(function_index); + instruction.set_b(function_register); + instruction.set_c(argument_count); instruction } @@ -557,11 +558,19 @@ impl Instruction { } Operation::Call => { 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)); } @@ -778,11 +787,12 @@ mod tests { #[test] fn call() { - let instruction = Instruction::call(4, 3); + let instruction = Instruction::call(1, 3, 4); assert_eq!(instruction.operation(), Operation::Call); - assert_eq!(instruction.a(), 4); + assert_eq!(instruction.a(), 1); assert_eq!(instruction.b(), 3); + assert_eq!(instruction.c(), 4); } #[test] diff --git a/dust-lang/src/operation.rs b/dust-lang/src/operation.rs index 8641f0a..d6be751 100644 --- a/dust-lang/src/operation.rs +++ b/dust-lang/src/operation.rs @@ -7,7 +7,7 @@ const LOAD_BOOLEAN: u8 = 0b0000_0010; const LOAD_CONSTANT: u8 = 0b0000_0011; 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 SET_LOCAL: u8 = 0b0000_0111; @@ -43,22 +43,22 @@ pub enum Operation { LoadList = LOAD_LIST as isize, // Variables - DefineLocal = DECLARE_LOCAL as isize, + DefineLocal = DEFINE_LOCAL as isize, GetLocal = GET_LOCAL as isize, SetLocal = SET_LOCAL as isize, - // Binary operations + // Binary math Add = ADD as isize, Subtract = SUBTRACT as isize, Multiply = MULTIPLY as isize, Divide = DIVIDE as isize, Modulo = MODULO as isize, - // Logical operations + // Binary logic Test = TEST as isize, TestSet = TEST_SET as isize, - // Relational operations + // Binary comparison Equal = EQUAL as isize, Less = LESS as isize, LessEqual = LESS_EQUAL as isize, @@ -105,7 +105,7 @@ impl From for Operation { LOAD_BOOLEAN => Operation::LoadBoolean, LOAD_CONSTANT => Operation::LoadConstant, LOAD_LIST => Operation::LoadList, - DECLARE_LOCAL => Operation::DefineLocal, + DEFINE_LOCAL => Operation::DefineLocal, GET_LOCAL => Operation::GetLocal, SET_LOCAL => Operation::SetLocal, ADD => Operation::Add, @@ -142,7 +142,7 @@ impl From for u8 { Operation::LoadBoolean => LOAD_BOOLEAN, Operation::LoadConstant => LOAD_CONSTANT, Operation::LoadList => LOAD_LIST, - Operation::DefineLocal => DECLARE_LOCAL, + Operation::DefineLocal => DEFINE_LOCAL, Operation::GetLocal => GET_LOCAL, Operation::SetLocal => SET_LOCAL, Operation::Add => ADD, diff --git a/dust-lang/src/parser.rs b/dust-lang/src/parser.rs index 95ab2f9..f50222e 100644 --- a/dust-lang/src/parser.rs +++ b/dust-lang/src/parser.rs @@ -183,21 +183,6 @@ impl<'src> Parser<'src> { let constant_index = self.chunk.push_constant(value, position)?; 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( Instruction::load_constant(register, constant_index, false), position, @@ -1201,21 +1186,21 @@ impl<'src> Parser<'src> { } let start = self.current_position.0; + let function_register = last_instruction.a(); self.advance()?; - let function_register = self.next_register(); - while !self.allow(Token::RightParenthesis)? { self.parse_expression()?; self.allow(Token::Comma)?; } 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( - Instruction::call(register, function_register), + Instruction::call(to_register, function_register, argument_count), Span(start, end), ); diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index f7a6694..cd0bdf5 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -347,8 +347,9 @@ impl Vm { } Operation::Call => { let to_register = instruction.a(); - let function_index = instruction.b(); - let value = self.get(function_index, position)?.clone(); + let function_register = instruction.b(); + let argument_count = instruction.c(); + let value = self.get(function_register, position)?.clone(); let function = if let Value::Function(function) = value { function } else { @@ -358,8 +359,11 @@ impl Vm { }); }; 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 top_of_stack = function_vm.stack.len() as u8; @@ -369,7 +373,7 @@ impl Vm { let return_value = function_vm.run()?; if let Some(value) = return_value { - self.set(function_index, value, position)?; + self.set(to_register, value, position)?; } } Operation::Return => { diff --git a/dust-lang/tests/expressions.rs b/dust-lang/tests/expressions.rs index 32a22c4..7f8f6b7 100644 --- a/dust-lang/tests/expressions.rs +++ b/dust-lang/tests/expressions.rs @@ -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] fn greater() { let source = "1 > 2"; @@ -416,7 +462,7 @@ fn if_else_expression() { ), (Instruction::jump(1, true), Span(5, 7)), (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)), ], vec![ diff --git a/examples/fibonacci.ds b/examples/fibonacci.ds new file mode 100644 index 0000000..1eb0f81 --- /dev/null +++ b/examples/fibonacci.ds @@ -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)