From 8db37bcdfd15ac78e36a0c50a15251cacb1b60b7 Mon Sep 17 00:00:00 2001 From: Jeff Date: Sun, 20 Oct 2024 02:30:22 -0400 Subject: [PATCH] Add LoadSelf instruction to enable recursion --- dust-lang/src/chunk.rs | 10 ++---- dust-lang/src/instruction.rs | 40 ++++++++++++++++++++++++ dust-lang/src/operation.rs | 41 ++++++++++++++----------- dust-lang/src/parser.rs | 59 ++++++++++++++++++++++-------------- dust-lang/src/vm.rs | 17 +++++++++-- examples/fibonacci.ds | 6 ++-- 6 files changed, 121 insertions(+), 52 deletions(-) diff --git a/dust-lang/src/chunk.rs b/dust-lang/src/chunk.rs index cf77ffc..c1acc7c 100644 --- a/dust-lang/src/chunk.rs +++ b/dust-lang/src/chunk.rs @@ -137,24 +137,20 @@ impl Chunk { .map(|local| &local.identifier) } - pub fn get_local_index( - &self, - identifier: &Identifier, - position: Span, - ) -> Result { + pub fn get_local_index(&self, identifier_text: &str, position: Span) -> Result { self.locals .iter() .enumerate() .rev() .find_map(|(index, local)| { - if &local.identifier == identifier { + if local.identifier.as_str() == identifier_text { Some(index as u8) } else { None } }) .ok_or(ChunkError::IdentifierNotFound { - identifier: identifier.clone(), + identifier: Identifier::new(identifier_text), position, }) } diff --git a/dust-lang/src/instruction.rs b/dust-lang/src/instruction.rs index 04e5be3..03a0b50 100644 --- a/dust-lang/src/instruction.rs +++ b/dust-lang/src/instruction.rs @@ -54,6 +54,14 @@ impl Instruction { instruction } + pub fn load_self(to_register: u8) -> Instruction { + let mut instruction = Instruction(Operation::LoadSelf as u32); + + instruction.set_a(to_register); + + instruction + } + pub fn define_local(to_register: u8, local_index: u8, is_mutable: bool) -> Instruction { let mut instruction = Instruction(Operation::DefineLocal as u32); @@ -329,6 +337,7 @@ impl Instruction { | Operation::LoadBoolean | Operation::LoadConstant | Operation::LoadList + | Operation::LoadSelf | Operation::Modulo | Operation::Multiply | Operation::Negate @@ -396,6 +405,19 @@ impl Instruction { Some(format!("R{to_register} = [R{first_index}..=R{last_index}]",)) } + Operation::LoadSelf => { + let to_register = self.a(); + let name = chunk + .map(|chunk| { + chunk + .name() + .map(|idenifier| idenifier.as_str()) + .unwrap_or("self") + }) + .unwrap(); + + Some(format!("R{to_register} = {name}")) + } Operation::DefineLocal => { let to_register = self.a(); let local_index = self.b(); @@ -643,6 +665,24 @@ mod tests { assert!(instruction.c_as_boolean()); } + #[test] + fn load_list() { + let instruction = Instruction::load_list(0, 1, 2); + + assert_eq!(instruction.operation(), Operation::LoadList); + assert_eq!(instruction.a(), 0); + assert_eq!(instruction.b(), 1); + assert_eq!(instruction.c(), 2); + } + + #[test] + fn load_self() { + let instruction = Instruction::load_self(10); + + assert_eq!(instruction.operation(), Operation::LoadSelf); + assert_eq!(instruction.a(), 10); + } + #[test] fn declare_local() { let mut instruction = Instruction::define_local(0, 1, true); diff --git a/dust-lang/src/operation.rs b/dust-lang/src/operation.rs index d6be751..72f05cc 100644 --- a/dust-lang/src/operation.rs +++ b/dust-lang/src/operation.rs @@ -6,30 +6,31 @@ const CLOSE: u8 = 0b000_0001; const LOAD_BOOLEAN: u8 = 0b0000_0010; const LOAD_CONSTANT: u8 = 0b0000_0011; const LOAD_LIST: u8 = 0b0000_0100; +const LOAD_SELF: u8 = 0b0000_0101; -const DEFINE_LOCAL: u8 = 0b0000_0101; -const GET_LOCAL: u8 = 0b0000_0110; -const SET_LOCAL: u8 = 0b0000_0111; +const DEFINE_LOCAL: u8 = 0b0000_0110; +const GET_LOCAL: u8 = 0b0000_0111; +const SET_LOCAL: u8 = 0b0000_1000; -const ADD: u8 = 0b0000_1000; -const SUBTRACT: u8 = 0b0000_1001; -const MULTIPLY: u8 = 0b0000_1010; -const DIVIDE: u8 = 0b0000_1011; -const MODULO: u8 = 0b0000_1100; +const ADD: u8 = 0b0000_1001; +const SUBTRACT: u8 = 0b0000_1010; +const MULTIPLY: u8 = 0b0000_1011; +const DIVIDE: u8 = 0b0000_1100; +const MODULO: u8 = 0b0000_1101; -const TEST: u8 = 0b0000_1101; -const TEST_SET: u8 = 0b0000_1110; +const TEST: u8 = 0b0000_1110; +const TEST_SET: u8 = 0b0000_1111; -const EQUAL: u8 = 0b0000_1111; -const LESS: u8 = 0b0001_0000; -const LESS_EQUAL: u8 = 0b0001_0001; +const EQUAL: u8 = 0b0001_0000; +const LESS: u8 = 0b0001_0001; +const LESS_EQUAL: u8 = 0b0001_0010; -const NEGATE: u8 = 0b0001_0010; -const NOT: u8 = 0b0001_0011; +const NEGATE: u8 = 0b0001_0011; +const NOT: u8 = 0b0001_0100; -const JUMP: u8 = 0b0001_0100; -const CALL: u8 = 0b0001_0101; -const RETURN: u8 = 0b0001_0110; +const JUMP: u8 = 0b0001_0101; +const CALL: u8 = 0b0001_0110; +const RETURN: u8 = 0b0001_0111; #[derive(Clone, Copy, Debug, PartialEq)] pub enum Operation { @@ -41,6 +42,7 @@ pub enum Operation { LoadBoolean = LOAD_BOOLEAN as isize, LoadConstant = LOAD_CONSTANT as isize, LoadList = LOAD_LIST as isize, + LoadSelf = LOAD_SELF as isize, // Variables DefineLocal = DEFINE_LOCAL as isize, @@ -105,6 +107,7 @@ impl From for Operation { LOAD_BOOLEAN => Operation::LoadBoolean, LOAD_CONSTANT => Operation::LoadConstant, LOAD_LIST => Operation::LoadList, + LOAD_SELF => Operation::LoadSelf, DEFINE_LOCAL => Operation::DefineLocal, GET_LOCAL => Operation::GetLocal, SET_LOCAL => Operation::SetLocal, @@ -142,6 +145,7 @@ impl From for u8 { Operation::LoadBoolean => LOAD_BOOLEAN, Operation::LoadConstant => LOAD_CONSTANT, Operation::LoadList => LOAD_LIST, + Operation::LoadSelf => LOAD_SELF, Operation::DefineLocal => DEFINE_LOCAL, Operation::GetLocal => GET_LOCAL, Operation::SetLocal => SET_LOCAL, @@ -172,6 +176,7 @@ impl Display for Operation { Operation::LoadBoolean => write!(f, "LOAD_BOOLEAN"), Operation::LoadConstant => write!(f, "LOAD_CONSTANT"), Operation::LoadList => write!(f, "LOAD_LIST"), + Operation::LoadSelf => write!(f, "LOAD_SELF"), Operation::DefineLocal => write!(f, "DEFINE_LOCAL"), Operation::GetLocal => write!(f, "GET_LOCAL"), Operation::SetLocal => write!(f, "SET_LOCAL"), diff --git a/dust-lang/src/parser.rs b/dust-lang/src/parser.rs index d107a9a..07e57f5 100644 --- a/dust-lang/src/parser.rs +++ b/dust-lang/src/parser.rs @@ -673,7 +673,43 @@ impl<'src> Parser<'src> { self.advance()?; - let local_index = self.parse_identifier_from(token, start_position)?; + let local_index = if let Token::Identifier(text) = token { + if let Ok(local_index) = self.chunk.get_local_index(text, start_position) { + local_index + } else if let Some(name) = self.chunk.name() { + if name.as_str() == text { + let register = self.next_register(); + + self.emit_instruction(Instruction::load_self(register), start_position); + + self.chunk.declare_local( + Identifier::new(text), + None, + false, + register, + start_position, + )?; + + return Ok(()); + } else { + return Err(ParseError::UndeclaredVariable { + identifier: Identifier::new(text), + position: start_position, + }); + } + } else { + return Err(ParseError::UndeclaredVariable { + identifier: Identifier::new(text), + position: start_position, + }); + } + } else { + return Err(ParseError::ExpectedToken { + expected: TokenKind::Identifier, + found: self.current_token.to_owned(), + position: start_position, + }); + }; let is_mutable = self .chunk .get_local(local_index, start_position)? @@ -737,27 +773,6 @@ impl<'src> Parser<'src> { Ok(()) } - fn parse_identifier_from(&mut self, token: Token, position: Span) -> Result { - if let Token::Identifier(text) = token { - let identifier = Identifier::new(text); - - if let Ok(local_index) = self.chunk.get_local_index(&identifier, position) { - Ok(local_index) - } else { - Err(ParseError::UndeclaredVariable { - identifier, - position, - }) - } - } else { - Err(ParseError::ExpectedToken { - expected: TokenKind::Identifier, - found: self.current_token.to_owned(), - position, - }) - } - } - fn parse_type_from(&mut self, token: Token, position: Span) -> Result { match token { Token::Bool => Ok(Type::Boolean), diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index 97e5343..df46596 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -1,8 +1,8 @@ use std::{cmp::Ordering, mem::replace}; use crate::{ - parse, value::Primitive, AnnotatedError, Chunk, ChunkError, DustError, Identifier, Instruction, - Operation, Span, Type, Value, ValueError, + parse, value::Primitive, AnnotatedError, Chunk, ChunkError, DustError, FunctionType, + Identifier, Instruction, Operation, Span, Type, Value, ValueError, }; pub fn run(source: &str) -> Result, DustError> { @@ -116,6 +116,19 @@ impl Vm { self.set(to_register, value, position)?; } + Operation::LoadSelf => { + let to_register = instruction.a(); + let value = Value::function( + self.chunk.clone(), + FunctionType { + type_parameters: None, + value_parameters: None, + return_type: None, + }, + ); + + self.set(to_register, value, position)?; + } Operation::DefineLocal => { let from_register = instruction.a(); let to_local = instruction.b(); diff --git a/examples/fibonacci.ds b/examples/fibonacci.ds index 1eb0f81..e352fbf 100644 --- a/examples/fibonacci.ds +++ b/examples/fibonacci.ds @@ -1,11 +1,11 @@ -let fib = fn (n: int) -> int { - if n == 0 { +fn fib (n: int) -> int { + if n <= 0 { return 0; } else if n == 1 { return 1; } else { return fib(n - 1) + fib(n - 2); } -}; +} fib(10)