From ea0be431999850b7336699278f20359fa57b7c85 Mon Sep 17 00:00:00 2001 From: Jeff Date: Sat, 12 Oct 2024 10:55:34 -0400 Subject: [PATCH] Begin adding function calls --- dust-lang/src/chunk.rs | 16 ++-- dust-lang/src/instruction.rs | 37 +++++++++ dust-lang/src/operation.rs | 7 +- dust-lang/src/parser.rs | 147 +++++++++++++++++---------------- dust-lang/src/value.rs | 6 +- dust-lang/src/vm.rs | 49 +++++++---- dust-lang/tests/expressions.rs | 32 +++---- 7 files changed, 175 insertions(+), 119 deletions(-) diff --git a/dust-lang/src/chunk.rs b/dust-lang/src/chunk.rs index 3f5f8f7..0ad186c 100644 --- a/dust-lang/src/chunk.rs +++ b/dust-lang/src/chunk.rs @@ -227,7 +227,7 @@ impl Chunk { r#type, is_mutable, self.scope_depth, - Some(register_index), + register_index, )); Ok(starting_length as u8) @@ -249,7 +249,7 @@ impl Chunk { log::debug!("Define local {}", local.identifier); - local.register_index = Some(register_index); + local.register_index = register_index; Ok(()) } @@ -309,7 +309,7 @@ pub struct Local { pub r#type: Option, pub is_mutable: bool, pub depth: usize, - pub register_index: Option, + pub register_index: u8, } impl Local { @@ -318,7 +318,7 @@ impl Local { r#type: Option, mutable: bool, depth: usize, - register_index: Option, + register_index: u8, ) -> Self { Self { identifier, @@ -499,17 +499,13 @@ impl<'a> ChunkDisassembler<'a> { }, ) in self.chunk.locals.iter().enumerate() { - let register_display = register_index - .as_ref() - .map(|value| value.to_string()) - .unwrap_or_else(|| "empty".to_string()); let identifier_display = identifier.as_str(); let type_display = r#type .as_ref() .map(|r#type| r#type.to_string()) .unwrap_or("unknown".to_string()); let local_display = format!( - "{index:<5} {identifier_display:10} {type_display:8} {mutable:7} {depth:<5} {register_display:8}" + "{index:<5} {identifier_display:10} {type_display:8} {mutable:7} {depth:<5} {register_index:8}" ); push(&local_display, false); @@ -541,7 +537,7 @@ impl<'a> ChunkDisassembler<'a> { value_option.as_ref().and_then(|value| match value { Value::Primitive(value_data) => value_data.as_function().map(|function| { function - .body + .chunk .disassembler("function") .styled(self.styled) .indent(self.indent + 1) diff --git a/dust-lang/src/instruction.rs b/dust-lang/src/instruction.rs index 3d6bb2a..571c6aa 100644 --- a/dust-lang/src/instruction.rs +++ b/dust-lang/src/instruction.rs @@ -208,6 +208,15 @@ impl Instruction { instruction } + pub fn call(function_index: u8, argument_count: u8) -> Instruction { + let mut instruction = Instruction(Operation::Call as u32); + + instruction.set_a(function_index); + instruction.set_b(argument_count); + + instruction + } + pub fn r#return(should_return_value: bool) -> Instruction { let mut instruction = Instruction(Operation::Return as u32); @@ -572,6 +581,25 @@ impl Instruction { None } + Operation::Call => { + let function_index = self.a(); + let argument_count = self.b(); + let last_argument = function_index + argument_count; + + let mut output = format!("R{function_index}("); + + for register in function_index..last_argument { + if register != last_argument - 1 { + output.push_str(", "); + } + + output.push_str(&format!("R{}", register)); + } + + output.push(')'); + + Some(output) + } Operation::Return => None, }; @@ -779,6 +807,15 @@ mod tests { assert!(instruction.c_as_boolean()); } + #[test] + fn call() { + let instruction = Instruction::call(4, 1); + + assert_eq!(instruction.operation(), Operation::Call); + assert_eq!(instruction.a(), 4); + assert_eq!(instruction.b(), 1); + } + #[test] fn r#return() { let instruction = Instruction::r#return(true); diff --git a/dust-lang/src/operation.rs b/dust-lang/src/operation.rs index cf43bff..8641f0a 100644 --- a/dust-lang/src/operation.rs +++ b/dust-lang/src/operation.rs @@ -28,7 +28,8 @@ const NEGATE: u8 = 0b0001_0010; const NOT: u8 = 0b0001_0011; const JUMP: u8 = 0b0001_0100; -const RETURN: u8 = 0b0001_0101; +const CALL: u8 = 0b0001_0101; +const RETURN: u8 = 0b0001_0110; #[derive(Clone, Copy, Debug, PartialEq)] pub enum Operation { @@ -68,6 +69,7 @@ pub enum Operation { // Control flow Jump = JUMP as isize, + Call = CALL as isize, Return = RETURN as isize, } @@ -119,6 +121,7 @@ impl From for Operation { NEGATE => Operation::Negate, NOT => Operation::Not, JUMP => Operation::Jump, + CALL => Operation::Call, RETURN => Operation::Return, _ => { if cfg!(test) { @@ -155,6 +158,7 @@ impl From for u8 { Operation::Negate => NEGATE, Operation::Not => NOT, Operation::Jump => JUMP, + Operation::Call => CALL, Operation::Return => RETURN, } } @@ -184,6 +188,7 @@ impl Display for Operation { Operation::Negate => write!(f, "NEGATE"), Operation::Not => write!(f, "NOT"), Operation::Jump => write!(f, "JUMP"), + Operation::Call => write!(f, "CALL"), Operation::Return => write!(f, "RETURN"), } } diff --git a/dust-lang/src/parser.rs b/dust-lang/src/parser.rs index de3fb7e..7bf5cc4 100644 --- a/dust-lang/src/parser.rs +++ b/dust-lang/src/parser.rs @@ -303,14 +303,7 @@ impl<'src> Parser<'src> { fn parse_grouped(&mut self, _: Allowed) -> Result<(), ParseError> { self.allow(Token::LeftParenthesis)?; - self.parse_statement( - Allowed { - assignment: false, - explicit_return: false, - implicit_return: false, - }, - Context::None, - )?; + self.parse_expression()?; self.expect(Token::RightParenthesis)?; self.parsed_expression = true; @@ -323,14 +316,7 @@ impl<'src> Parser<'src> { let operator_position = self.current_position; self.advance()?; - self.parse_statement( - Allowed { - assignment: false, - explicit_return: false, - implicit_return: false, - }, - Context::None, - )?; + self.parse_expression()?; let (previous_instruction, previous_position) = self.chunk.pop_instruction(self.current_position)?; @@ -394,11 +380,7 @@ impl<'src> Parser<'src> { let local = self.chunk.get_local(local_index, self.current_position)?; is_mutable_local = local.is_mutable; - if let Some(index) = local.register_index { - index - } else { - instruction.a() - } + local.register_index } Operation::LoadConstant => { is_constant = true; @@ -705,32 +687,23 @@ impl<'src> Parser<'src> { }); } - self.parse_statement( - Allowed { - assignment: false, - explicit_return: true, - implicit_return: false, - }, - Context::Assignment, - )?; + self.parse_expression()?; let (mut previous_instruction, previous_position) = self.chunk.pop_instruction(self.current_position)?; if previous_instruction.operation().is_math() { - let previous_register = self + let register_index = self .chunk .get_local(local_index, start_position)? .register_index; - if let Some(register_index) = previous_register { - log::trace!("Condensing SET_LOCAL to binary math expression"); + log::trace!("Condensing SET_LOCAL to binary math expression"); - previous_instruction.set_a(register_index); - self.emit_instruction(previous_instruction, self.current_position); + previous_instruction.set_a(register_index); + self.emit_instruction(previous_instruction, self.current_position); - return Ok(()); - } + return Ok(()); } self.emit_instruction(previous_instruction, previous_position); @@ -815,14 +788,7 @@ impl<'src> Parser<'src> { while !self.allow(Token::RightSquareBrace)? && !self.is_eof() { let next_register = self.current_register; - self.parse_statement( - Allowed { - assignment: false, - explicit_return: false, - implicit_return: false, - }, - Context::None, - )?; + self.parse_expression()?; if let Operation::LoadConstant = self.chunk.get_last_operation()? { self.increment_register()?; @@ -853,21 +819,10 @@ impl<'src> Parser<'src> { } fn parse_if(&mut self, allowed: Allowed) -> Result<(), ParseError> { - let length = self.chunk.len(); - let expression_allowed = Allowed { - assignment: false, - explicit_return: false, - implicit_return: false, - }; - let block_allowed = Allowed { - assignment: allowed.assignment, - explicit_return: allowed.explicit_return, - implicit_return: false, - }; - self.advance()?; - self.parse_statement(expression_allowed, Context::None)?; + self.parse_expression()?; + let length = self.chunk.len(); let is_explicit_boolean = matches!(self.previous_token, Token::Boolean(_)) && length == self.chunk.len() - 1; @@ -878,6 +833,12 @@ impl<'src> Parser<'src> { ); } + let block_allowed = Allowed { + assignment: allowed.assignment, + explicit_return: allowed.explicit_return, + implicit_return: false, + }; + if let Token::LeftCurlyBrace = self.current_token { self.parse_block(block_allowed)?; } @@ -917,14 +878,7 @@ impl<'src> Parser<'src> { let jump_start = self.chunk.len(); - self.parse_statement( - Allowed { - assignment: false, - explicit_return: false, - implicit_return: false, - }, - Context::None, - )?; + self.parse_expression()?; self.parse_block(Allowed { assignment: true, explicit_return: allowed.explicit_return, @@ -988,6 +942,17 @@ impl<'src> Parser<'src> { Ok(()) } + fn parse_expression(&mut self) -> Result<(), ParseError> { + self.parse( + Precedence::None, + Allowed { + assignment: false, + explicit_return: false, + implicit_return: false, + }, + ) + } + fn parse_return(&mut self, allowed: Allowed) -> Result<(), ParseError> { let start = self.current_position.0; @@ -1120,7 +1085,8 @@ impl<'src> Parser<'src> { function_parser.advance()?; let end = function_parser.current_position.1; - let local_index = function_parser.chunk.declare_local( + + function_parser.chunk.declare_local( parameter, Some(r#type), is_mutable, @@ -1128,11 +1094,6 @@ impl<'src> Parser<'src> { Span(start, end), )?; - function_parser.chunk.define_local( - local_index, - function_parser.current_register, - Span(start, end), - )?; function_parser.increment_register()?; function_parser.allow(Token::Comma)?; } @@ -1169,6 +1130,46 @@ impl<'src> Parser<'src> { Ok(()) } + fn parse_call(&mut self) -> Result<(), ParseError> { + self.advance()?; + + let function_register = self.current_register; + let mut argument_count = 0; + + while !self.allow(Token::RightParenthesis)? { + if argument_count > 0 { + self.expect(Token::Comma)?; + } + + let register = self.current_register; + + self.parse( + Precedence::None, + Allowed { + assignment: false, + explicit_return: false, + implicit_return: false, + }, + )?; + + if self.current_register == register { + return Err(ParseError::ExpectedExpression { + found: self.previous_token.to_owned(), + position: self.previous_position, + }); + } + + argument_count += 1; + } + + self.emit_instruction( + Instruction::call(function_register, argument_count), + self.current_position, + ); + + Ok(()) + } + fn parse(&mut self, precedence: Precedence, allowed: Allowed) -> Result<(), ParseError> { if let Some(prefix_parser) = ParseRule::from(&self.current_token).prefix { log::debug!( @@ -1384,8 +1385,8 @@ impl From<&Token<'_>> for ParseRule<'_> { }, Token::LeftParenthesis => ParseRule { prefix: Some(Parser::parse_grouped), - infix: None, - precedence: Precedence::None, + infix: Some(Parser::parse_call), + precedence: Precedence::Call, }, Token::LeftSquareBrace => ParseRule { prefix: Some(Parser::parse_list), diff --git a/dust-lang/src/value.rs b/dust-lang/src/value.rs index 4cd5c79..68bd5b6 100644 --- a/dust-lang/src/value.rs +++ b/dust-lang/src/value.rs @@ -60,7 +60,7 @@ impl Value { } pub fn function(body: Chunk) -> Self { - Value::Primitive(Primitive::Function(Function { body })) + Value::Primitive(Primitive::Function(Function { chunk: body })) } pub fn integer>(into_i64: T) -> Self { @@ -286,7 +286,7 @@ impl From<&str> for Value { impl Clone for Value { fn clone(&self) -> Self { - log::trace!("Cloning value {:?}", self); + log::trace!("Cloning value {self}"); match self { Value::Primitive(data) => Value::Primitive(data.clone()), @@ -643,7 +643,7 @@ impl Ord for Primitive { #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub struct Function { - pub body: Chunk, + pub chunk: Chunk, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index a4642c3..fb16dd9 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -7,7 +7,6 @@ use crate::{ pub fn run(source: &str) -> Result, DustError> { let chunk = parse(source)?; - let mut vm = Vm::new(chunk); vm.run() @@ -32,10 +31,6 @@ impl Vm { } } - pub fn take_chunk(self) -> Chunk { - self.chunk - } - pub fn run(&mut self) -> Result, VmError> { // DRY helper to get constant or register values for binary operations fn get_arguments( @@ -131,14 +126,7 @@ impl Vm { let to_register = instruction.a(); let local_index = instruction.b(); let local = self.chunk.get_local(local_index, position)?; - let from_register = - local - .register_index - .ok_or_else(|| VmError::UndefinedVariable { - identifier: local.identifier.clone(), - position, - })?; - let value = self.take(from_register, to_register, position)?; + let value = self.take(local.register_index, to_register, position)?; self.set(to_register, value, position)?; } @@ -380,11 +368,38 @@ impl Vm { self.ip = new_ip; } + Operation::Call => { + let function_index = instruction.a(); + let argument_count = instruction.b(); + let function = if let Value::Primitive(Primitive::Function(function)) = + self.get(function_index, position)?.clone() + { + function + } else { + todo!() + }; + let mut function_vm = Vm::new(function.chunk); + let first_argument_index = function_index + 1; + let last_argument_index = first_argument_index + argument_count; + + for argument_index in first_argument_index..=last_argument_index { + let argument = self.empty(argument_index, position)?; + + function_vm.stack.push(Register::Value(argument)); + } + + let return_value = function_vm.run()?; + + if let Some(value) = return_value { + self.set(function_index, value, position)?; + } + } Operation::Return => { let should_return_value = instruction.b_as_boolean(); + let top_of_stack = (self.stack.len() - 1) as u8; return if should_return_value { - let value = self.empty(self.stack.len() - 1, position)?; + let value = self.empty(top_of_stack, position)?; Ok(Some(value)) } else { @@ -507,7 +522,9 @@ impl Vm { } } - fn empty(&mut self, index: usize, position: Span) -> Result { + fn empty(&mut self, index: u8, position: Span) -> Result { + let index = index as usize; + if index >= self.stack.len() { return Err(VmError::RegisterIndexOutOfBounds { position }); } @@ -517,7 +534,7 @@ impl Vm { match register { Register::Value(value) => Ok(value), Register::Pointer(register_index) => { - let value = self.empty(register_index as usize, position)?; + let value = self.empty(register_index, position)?; Ok(value) } diff --git a/dust-lang/tests/expressions.rs b/dust-lang/tests/expressions.rs index 72aee78..7c5caa0 100644 --- a/dust-lang/tests/expressions.rs +++ b/dust-lang/tests/expressions.rs @@ -39,7 +39,7 @@ fn add_assign() { (Instruction::r#return(true), Span(24, 24)) ], vec![Value::integer(1), Value::integer(2)], - vec![Local::new(Identifier::new("a"), None, true, 0, Some(0))] + vec![Local::new(Identifier::new("a"), None, true, 0, 0)] )) ); @@ -105,11 +105,11 @@ fn block_scope() { Value::integer(1) ], vec![ - Local::new(Identifier::new("a"), None, false, 0, Some(0)), - Local::new(Identifier::new("b"), None, false, 1, Some(1)), - Local::new(Identifier::new("c"), None, false, 2, Some(2)), - Local::new(Identifier::new("d"), None, false, 1, Some(3)), - Local::new(Identifier::new("e"), None, false, 0, Some(4)), + Local::new(Identifier::new("a"), None, false, 0, 0), + Local::new(Identifier::new("b"), None, false, 1, 1), + Local::new(Identifier::new("c"), None, false, 2, 2), + Local::new(Identifier::new("d"), None, false, 1, 3), + Local::new(Identifier::new("e"), None, false, 0, 4), ] )), ); @@ -148,7 +148,7 @@ fn define_local() { (Instruction::define_local(0, 0, false), Span(4, 5)), ], vec![Value::integer(42)], - vec![Local::new(Identifier::new("x"), None, false, 0, Some(0))] + vec![Local::new(Identifier::new("x"), None, false, 0, 0)] )), ); @@ -197,7 +197,7 @@ fn divide_assign() { (Instruction::r#return(true), Span(24, 24)) ], vec![Value::integer(2), Value::integer(2)], - vec![Local::new(Identifier::new("a"), None, true, 0, Some(0))] + vec![Local::new(Identifier::new("a"), None, true, 0, 0)] )) ); @@ -261,7 +261,7 @@ fn equality_assignment_long() { (Instruction::r#return(true), Span(44, 44)), ], vec![Value::integer(4), Value::integer(4)], - vec![Local::new(Identifier::new("a"), None, false, 0, Some(0))] + vec![Local::new(Identifier::new("a"), None, false, 0, 0)] )), ); @@ -290,7 +290,7 @@ fn equality_assignment_short() { (Instruction::r#return(true), Span(16, 16)), ], vec![Value::integer(4), Value::integer(4)], - vec![Local::new(Identifier::new("a"), None, false, 0, Some(0))] + vec![Local::new(Identifier::new("a"), None, false, 0, 0)] )), ); @@ -664,7 +664,7 @@ fn multiply_assign() { (Instruction::r#return(true), Span(23, 23)) ], vec![Value::integer(2), Value::integer(3)], - vec![Local::new(Identifier::new("a"), None, true, 0, Some(0)),] + vec![Local::new(Identifier::new("a"), None, true, 0, 0),] )) ); @@ -803,7 +803,7 @@ fn set_local() { (Instruction::r#return(true), Span(25, 25)), ], vec![Value::integer(41), Value::integer(42)], - vec![Local::new(Identifier::new("x"), None, true, 0, Some(0)),] + vec![Local::new(Identifier::new("x"), None, true, 0, 0)] )), ); @@ -852,7 +852,7 @@ fn subtract_assign() { (Instruction::r#return(true), Span(25, 25)), ], vec![Value::integer(42), Value::integer(2)], - vec![Local::new(Identifier::new("x"), None, true, 0, Some(0)),] + vec![Local::new(Identifier::new("x"), None, true, 0, 0)] )), ); @@ -879,8 +879,8 @@ fn variable_and() { ], vec![], vec![ - Local::new(Identifier::new("a"), None, false, 0, Some(0)), - Local::new(Identifier::new("b"), None, false, 0, Some(1)), + Local::new(Identifier::new("a"), None, false, 0, 0), + Local::new(Identifier::new("b"), None, false, 0, 1), ] )) ); @@ -909,7 +909,7 @@ fn r#while() { (Instruction::r#return(true), Span(42, 42)), ], vec![Value::integer(0), Value::integer(5), Value::integer(1),], - vec![Local::new(Identifier::new("x"), None, true, 0, Some(0)),] + vec![Local::new(Identifier::new("x"), None, true, 0, 0),] )), );