1
0

Add LoadSelf instruction to enable recursion

This commit is contained in:
Jeff 2024-10-20 02:30:22 -04:00
parent 6caae6c952
commit 8db37bcdfd
6 changed files with 121 additions and 52 deletions

View File

@ -137,24 +137,20 @@ impl Chunk {
.map(|local| &local.identifier)
}
pub fn get_local_index(
&self,
identifier: &Identifier,
position: Span,
) -> Result<u8, ChunkError> {
pub fn get_local_index(&self, identifier_text: &str, position: Span) -> Result<u8, ChunkError> {
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,
})
}

View File

@ -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);

View File

@ -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<u8> 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<Operation> 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"),

View File

@ -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<u8, ParseError> {
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<Type, ParseError> {
match token {
Token::Bool => Ok(Type::Boolean),

View File

@ -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<Option<Value>, 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();

View File

@ -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)