Add LoadSelf instruction to enable recursion
This commit is contained in:
parent
6caae6c952
commit
8db37bcdfd
@ -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,
|
||||
})
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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"),
|
||||
|
@ -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),
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user