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)
|
.map(|local| &local.identifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_local_index(
|
pub fn get_local_index(&self, identifier_text: &str, position: Span) -> Result<u8, ChunkError> {
|
||||||
&self,
|
|
||||||
identifier: &Identifier,
|
|
||||||
position: Span,
|
|
||||||
) -> Result<u8, ChunkError> {
|
|
||||||
self.locals
|
self.locals
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.rev()
|
.rev()
|
||||||
.find_map(|(index, local)| {
|
.find_map(|(index, local)| {
|
||||||
if &local.identifier == identifier {
|
if local.identifier.as_str() == identifier_text {
|
||||||
Some(index as u8)
|
Some(index as u8)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.ok_or(ChunkError::IdentifierNotFound {
|
.ok_or(ChunkError::IdentifierNotFound {
|
||||||
identifier: identifier.clone(),
|
identifier: Identifier::new(identifier_text),
|
||||||
position,
|
position,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,14 @@ impl Instruction {
|
|||||||
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 {
|
pub fn define_local(to_register: u8, local_index: u8, is_mutable: bool) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::DefineLocal as u32);
|
let mut instruction = Instruction(Operation::DefineLocal as u32);
|
||||||
|
|
||||||
@ -329,6 +337,7 @@ impl Instruction {
|
|||||||
| Operation::LoadBoolean
|
| Operation::LoadBoolean
|
||||||
| Operation::LoadConstant
|
| Operation::LoadConstant
|
||||||
| Operation::LoadList
|
| Operation::LoadList
|
||||||
|
| Operation::LoadSelf
|
||||||
| Operation::Modulo
|
| Operation::Modulo
|
||||||
| Operation::Multiply
|
| Operation::Multiply
|
||||||
| Operation::Negate
|
| Operation::Negate
|
||||||
@ -396,6 +405,19 @@ impl Instruction {
|
|||||||
|
|
||||||
Some(format!("R{to_register} = [R{first_index}..=R{last_index}]",))
|
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 => {
|
Operation::DefineLocal => {
|
||||||
let to_register = self.a();
|
let to_register = self.a();
|
||||||
let local_index = self.b();
|
let local_index = self.b();
|
||||||
@ -643,6 +665,24 @@ mod tests {
|
|||||||
assert!(instruction.c_as_boolean());
|
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]
|
#[test]
|
||||||
fn declare_local() {
|
fn declare_local() {
|
||||||
let mut instruction = Instruction::define_local(0, 1, true);
|
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_BOOLEAN: u8 = 0b0000_0010;
|
||||||
const LOAD_CONSTANT: u8 = 0b0000_0011;
|
const LOAD_CONSTANT: u8 = 0b0000_0011;
|
||||||
const LOAD_LIST: u8 = 0b0000_0100;
|
const LOAD_LIST: u8 = 0b0000_0100;
|
||||||
|
const LOAD_SELF: u8 = 0b0000_0101;
|
||||||
|
|
||||||
const DEFINE_LOCAL: u8 = 0b0000_0101;
|
const DEFINE_LOCAL: u8 = 0b0000_0110;
|
||||||
const GET_LOCAL: u8 = 0b0000_0110;
|
const GET_LOCAL: u8 = 0b0000_0111;
|
||||||
const SET_LOCAL: u8 = 0b0000_0111;
|
const SET_LOCAL: u8 = 0b0000_1000;
|
||||||
|
|
||||||
const ADD: u8 = 0b0000_1000;
|
const ADD: u8 = 0b0000_1001;
|
||||||
const SUBTRACT: u8 = 0b0000_1001;
|
const SUBTRACT: u8 = 0b0000_1010;
|
||||||
const MULTIPLY: u8 = 0b0000_1010;
|
const MULTIPLY: u8 = 0b0000_1011;
|
||||||
const DIVIDE: u8 = 0b0000_1011;
|
const DIVIDE: u8 = 0b0000_1100;
|
||||||
const MODULO: u8 = 0b0000_1100;
|
const MODULO: u8 = 0b0000_1101;
|
||||||
|
|
||||||
const TEST: u8 = 0b0000_1101;
|
const TEST: u8 = 0b0000_1110;
|
||||||
const TEST_SET: u8 = 0b0000_1110;
|
const TEST_SET: u8 = 0b0000_1111;
|
||||||
|
|
||||||
const EQUAL: u8 = 0b0000_1111;
|
const EQUAL: u8 = 0b0001_0000;
|
||||||
const LESS: u8 = 0b0001_0000;
|
const LESS: u8 = 0b0001_0001;
|
||||||
const LESS_EQUAL: u8 = 0b0001_0001;
|
const LESS_EQUAL: u8 = 0b0001_0010;
|
||||||
|
|
||||||
const NEGATE: u8 = 0b0001_0010;
|
const NEGATE: u8 = 0b0001_0011;
|
||||||
const NOT: u8 = 0b0001_0011;
|
const NOT: u8 = 0b0001_0100;
|
||||||
|
|
||||||
const JUMP: u8 = 0b0001_0100;
|
const JUMP: u8 = 0b0001_0101;
|
||||||
const CALL: u8 = 0b0001_0101;
|
const CALL: u8 = 0b0001_0110;
|
||||||
const RETURN: u8 = 0b0001_0110;
|
const RETURN: u8 = 0b0001_0111;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum Operation {
|
pub enum Operation {
|
||||||
@ -41,6 +42,7 @@ pub enum Operation {
|
|||||||
LoadBoolean = LOAD_BOOLEAN as isize,
|
LoadBoolean = LOAD_BOOLEAN as isize,
|
||||||
LoadConstant = LOAD_CONSTANT as isize,
|
LoadConstant = LOAD_CONSTANT as isize,
|
||||||
LoadList = LOAD_LIST as isize,
|
LoadList = LOAD_LIST as isize,
|
||||||
|
LoadSelf = LOAD_SELF as isize,
|
||||||
|
|
||||||
// Variables
|
// Variables
|
||||||
DefineLocal = DEFINE_LOCAL as isize,
|
DefineLocal = DEFINE_LOCAL as isize,
|
||||||
@ -105,6 +107,7 @@ impl From<u8> for Operation {
|
|||||||
LOAD_BOOLEAN => Operation::LoadBoolean,
|
LOAD_BOOLEAN => Operation::LoadBoolean,
|
||||||
LOAD_CONSTANT => Operation::LoadConstant,
|
LOAD_CONSTANT => Operation::LoadConstant,
|
||||||
LOAD_LIST => Operation::LoadList,
|
LOAD_LIST => Operation::LoadList,
|
||||||
|
LOAD_SELF => Operation::LoadSelf,
|
||||||
DEFINE_LOCAL => Operation::DefineLocal,
|
DEFINE_LOCAL => Operation::DefineLocal,
|
||||||
GET_LOCAL => Operation::GetLocal,
|
GET_LOCAL => Operation::GetLocal,
|
||||||
SET_LOCAL => Operation::SetLocal,
|
SET_LOCAL => Operation::SetLocal,
|
||||||
@ -142,6 +145,7 @@ impl From<Operation> for u8 {
|
|||||||
Operation::LoadBoolean => LOAD_BOOLEAN,
|
Operation::LoadBoolean => LOAD_BOOLEAN,
|
||||||
Operation::LoadConstant => LOAD_CONSTANT,
|
Operation::LoadConstant => LOAD_CONSTANT,
|
||||||
Operation::LoadList => LOAD_LIST,
|
Operation::LoadList => LOAD_LIST,
|
||||||
|
Operation::LoadSelf => LOAD_SELF,
|
||||||
Operation::DefineLocal => DEFINE_LOCAL,
|
Operation::DefineLocal => DEFINE_LOCAL,
|
||||||
Operation::GetLocal => GET_LOCAL,
|
Operation::GetLocal => GET_LOCAL,
|
||||||
Operation::SetLocal => SET_LOCAL,
|
Operation::SetLocal => SET_LOCAL,
|
||||||
@ -172,6 +176,7 @@ impl Display for Operation {
|
|||||||
Operation::LoadBoolean => write!(f, "LOAD_BOOLEAN"),
|
Operation::LoadBoolean => write!(f, "LOAD_BOOLEAN"),
|
||||||
Operation::LoadConstant => write!(f, "LOAD_CONSTANT"),
|
Operation::LoadConstant => write!(f, "LOAD_CONSTANT"),
|
||||||
Operation::LoadList => write!(f, "LOAD_LIST"),
|
Operation::LoadList => write!(f, "LOAD_LIST"),
|
||||||
|
Operation::LoadSelf => write!(f, "LOAD_SELF"),
|
||||||
Operation::DefineLocal => write!(f, "DEFINE_LOCAL"),
|
Operation::DefineLocal => write!(f, "DEFINE_LOCAL"),
|
||||||
Operation::GetLocal => write!(f, "GET_LOCAL"),
|
Operation::GetLocal => write!(f, "GET_LOCAL"),
|
||||||
Operation::SetLocal => write!(f, "SET_LOCAL"),
|
Operation::SetLocal => write!(f, "SET_LOCAL"),
|
||||||
|
@ -673,7 +673,43 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
self.advance()?;
|
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
|
let is_mutable = self
|
||||||
.chunk
|
.chunk
|
||||||
.get_local(local_index, start_position)?
|
.get_local(local_index, start_position)?
|
||||||
@ -737,27 +773,6 @@ impl<'src> Parser<'src> {
|
|||||||
Ok(())
|
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> {
|
fn parse_type_from(&mut self, token: Token, position: Span) -> Result<Type, ParseError> {
|
||||||
match token {
|
match token {
|
||||||
Token::Bool => Ok(Type::Boolean),
|
Token::Bool => Ok(Type::Boolean),
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use std::{cmp::Ordering, mem::replace};
|
use std::{cmp::Ordering, mem::replace};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
parse, value::Primitive, AnnotatedError, Chunk, ChunkError, DustError, Identifier, Instruction,
|
parse, value::Primitive, AnnotatedError, Chunk, ChunkError, DustError, FunctionType,
|
||||||
Operation, Span, Type, Value, ValueError,
|
Identifier, Instruction, Operation, Span, Type, Value, ValueError,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn run(source: &str) -> Result<Option<Value>, DustError> {
|
pub fn run(source: &str) -> Result<Option<Value>, DustError> {
|
||||||
@ -116,6 +116,19 @@ impl Vm {
|
|||||||
|
|
||||||
self.set(to_register, value, position)?;
|
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 => {
|
Operation::DefineLocal => {
|
||||||
let from_register = instruction.a();
|
let from_register = instruction.a();
|
||||||
let to_local = instruction.b();
|
let to_local = instruction.b();
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
let fib = fn (n: int) -> int {
|
fn fib (n: int) -> int {
|
||||||
if n == 0 {
|
if n <= 0 {
|
||||||
return 0;
|
return 0;
|
||||||
} else if n == 1 {
|
} else if n == 1 {
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
return fib(n - 1) + fib(n - 2);
|
return fib(n - 1) + fib(n - 2);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
fib(10)
|
fib(10)
|
||||||
|
Loading…
Reference in New Issue
Block a user