Implement functions calls
This commit is contained in:
parent
65e513488f
commit
9d5c9d9fd0
@ -208,11 +208,12 @@ impl Instruction {
|
|||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn call(to_register: u8, function_index: u8) -> Instruction {
|
pub fn call(to_register: u8, function_register: u8, argument_count: u8) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::Call as u32);
|
let mut instruction = Instruction(Operation::Call as u32);
|
||||||
|
|
||||||
instruction.set_a(to_register);
|
instruction.set_a(to_register);
|
||||||
instruction.set_b(function_index);
|
instruction.set_b(function_register);
|
||||||
|
instruction.set_c(argument_count);
|
||||||
|
|
||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
@ -557,11 +558,19 @@ impl Instruction {
|
|||||||
}
|
}
|
||||||
Operation::Call => {
|
Operation::Call => {
|
||||||
let to_register = self.a();
|
let to_register = self.a();
|
||||||
let function_index = self.b();
|
let function_register = self.b();
|
||||||
|
let argument_count = self.c();
|
||||||
|
|
||||||
let mut output = format!("R{function_index}(");
|
let mut output = format!("R{to_register} = R{function_register}(");
|
||||||
|
let first_argument = function_register + 1;
|
||||||
|
|
||||||
|
for (index, register) in
|
||||||
|
(first_argument..first_argument + argument_count).enumerate()
|
||||||
|
{
|
||||||
|
if index > 0 {
|
||||||
|
output.push_str(", ");
|
||||||
|
}
|
||||||
|
|
||||||
for register in function_index + 1..to_register {
|
|
||||||
output.push_str(&format!("R{}", register));
|
output.push_str(&format!("R{}", register));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -778,11 +787,12 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn call() {
|
fn call() {
|
||||||
let instruction = Instruction::call(4, 3);
|
let instruction = Instruction::call(1, 3, 4);
|
||||||
|
|
||||||
assert_eq!(instruction.operation(), Operation::Call);
|
assert_eq!(instruction.operation(), Operation::Call);
|
||||||
assert_eq!(instruction.a(), 4);
|
assert_eq!(instruction.a(), 1);
|
||||||
assert_eq!(instruction.b(), 3);
|
assert_eq!(instruction.b(), 3);
|
||||||
|
assert_eq!(instruction.c(), 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -7,7 +7,7 @@ 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 DECLARE_LOCAL: u8 = 0b0000_0101;
|
const DEFINE_LOCAL: u8 = 0b0000_0101;
|
||||||
const GET_LOCAL: u8 = 0b0000_0110;
|
const GET_LOCAL: u8 = 0b0000_0110;
|
||||||
const SET_LOCAL: u8 = 0b0000_0111;
|
const SET_LOCAL: u8 = 0b0000_0111;
|
||||||
|
|
||||||
@ -43,22 +43,22 @@ pub enum Operation {
|
|||||||
LoadList = LOAD_LIST as isize,
|
LoadList = LOAD_LIST as isize,
|
||||||
|
|
||||||
// Variables
|
// Variables
|
||||||
DefineLocal = DECLARE_LOCAL as isize,
|
DefineLocal = DEFINE_LOCAL as isize,
|
||||||
GetLocal = GET_LOCAL as isize,
|
GetLocal = GET_LOCAL as isize,
|
||||||
SetLocal = SET_LOCAL as isize,
|
SetLocal = SET_LOCAL as isize,
|
||||||
|
|
||||||
// Binary operations
|
// Binary math
|
||||||
Add = ADD as isize,
|
Add = ADD as isize,
|
||||||
Subtract = SUBTRACT as isize,
|
Subtract = SUBTRACT as isize,
|
||||||
Multiply = MULTIPLY as isize,
|
Multiply = MULTIPLY as isize,
|
||||||
Divide = DIVIDE as isize,
|
Divide = DIVIDE as isize,
|
||||||
Modulo = MODULO as isize,
|
Modulo = MODULO as isize,
|
||||||
|
|
||||||
// Logical operations
|
// Binary logic
|
||||||
Test = TEST as isize,
|
Test = TEST as isize,
|
||||||
TestSet = TEST_SET as isize,
|
TestSet = TEST_SET as isize,
|
||||||
|
|
||||||
// Relational operations
|
// Binary comparison
|
||||||
Equal = EQUAL as isize,
|
Equal = EQUAL as isize,
|
||||||
Less = LESS as isize,
|
Less = LESS as isize,
|
||||||
LessEqual = LESS_EQUAL as isize,
|
LessEqual = LESS_EQUAL as isize,
|
||||||
@ -105,7 +105,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,
|
||||||
DECLARE_LOCAL => Operation::DefineLocal,
|
DEFINE_LOCAL => Operation::DefineLocal,
|
||||||
GET_LOCAL => Operation::GetLocal,
|
GET_LOCAL => Operation::GetLocal,
|
||||||
SET_LOCAL => Operation::SetLocal,
|
SET_LOCAL => Operation::SetLocal,
|
||||||
ADD => Operation::Add,
|
ADD => Operation::Add,
|
||||||
@ -142,7 +142,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::DefineLocal => DECLARE_LOCAL,
|
Operation::DefineLocal => DEFINE_LOCAL,
|
||||||
Operation::GetLocal => GET_LOCAL,
|
Operation::GetLocal => GET_LOCAL,
|
||||||
Operation::SetLocal => SET_LOCAL,
|
Operation::SetLocal => SET_LOCAL,
|
||||||
Operation::Add => ADD,
|
Operation::Add => ADD,
|
||||||
|
@ -183,21 +183,6 @@ impl<'src> Parser<'src> {
|
|||||||
let constant_index = self.chunk.push_constant(value, position)?;
|
let constant_index = self.chunk.push_constant(value, position)?;
|
||||||
let register = self.next_register();
|
let register = self.next_register();
|
||||||
|
|
||||||
if let Some(instruction) = self
|
|
||||||
.current_statement
|
|
||||||
.last()
|
|
||||||
.map(|(instruction, _)| instruction)
|
|
||||||
{
|
|
||||||
if let Operation::LoadConstant = instruction.operation() {
|
|
||||||
self.push_instruction(
|
|
||||||
Instruction::load_constant(instruction.a(), constant_index, false),
|
|
||||||
position,
|
|
||||||
);
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.push_instruction(
|
self.push_instruction(
|
||||||
Instruction::load_constant(register, constant_index, false),
|
Instruction::load_constant(register, constant_index, false),
|
||||||
position,
|
position,
|
||||||
@ -1201,21 +1186,21 @@ impl<'src> Parser<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let start = self.current_position.0;
|
let start = self.current_position.0;
|
||||||
|
let function_register = last_instruction.a();
|
||||||
|
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
|
|
||||||
let function_register = self.next_register();
|
|
||||||
|
|
||||||
while !self.allow(Token::RightParenthesis)? {
|
while !self.allow(Token::RightParenthesis)? {
|
||||||
self.parse_expression()?;
|
self.parse_expression()?;
|
||||||
self.allow(Token::Comma)?;
|
self.allow(Token::Comma)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let end = self.current_position.1;
|
let end = self.current_position.1;
|
||||||
let register = self.next_register();
|
let argument_count = self.next_register() - function_register - 1;
|
||||||
|
let to_register = self.next_register();
|
||||||
|
|
||||||
self.push_instruction(
|
self.push_instruction(
|
||||||
Instruction::call(register, function_register),
|
Instruction::call(to_register, function_register, argument_count),
|
||||||
Span(start, end),
|
Span(start, end),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -347,8 +347,9 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
Operation::Call => {
|
Operation::Call => {
|
||||||
let to_register = instruction.a();
|
let to_register = instruction.a();
|
||||||
let function_index = instruction.b();
|
let function_register = instruction.b();
|
||||||
let value = self.get(function_index, position)?.clone();
|
let argument_count = instruction.c();
|
||||||
|
let value = self.get(function_register, position)?.clone();
|
||||||
let function = if let Value::Function(function) = value {
|
let function = if let Value::Function(function) = value {
|
||||||
function
|
function
|
||||||
} else {
|
} else {
|
||||||
@ -358,8 +359,11 @@ impl Vm {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
let mut function_vm = Vm::new(function.take_chunk());
|
let mut function_vm = Vm::new(function.take_chunk());
|
||||||
|
let first_argument_index = function_register + 1;
|
||||||
|
|
||||||
for argument_index in function_index + 1..to_register {
|
for argument_index in
|
||||||
|
first_argument_index..first_argument_index + argument_count
|
||||||
|
{
|
||||||
let argument = self.get(argument_index, position)?.clone();
|
let argument = self.get(argument_index, position)?.clone();
|
||||||
let top_of_stack = function_vm.stack.len() as u8;
|
let top_of_stack = function_vm.stack.len() as u8;
|
||||||
|
|
||||||
@ -369,7 +373,7 @@ impl Vm {
|
|||||||
let return_value = function_vm.run()?;
|
let return_value = function_vm.run()?;
|
||||||
|
|
||||||
if let Some(value) = return_value {
|
if let Some(value) = return_value {
|
||||||
self.set(function_index, value, position)?;
|
self.set(to_register, value, position)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::Return => {
|
Operation::Return => {
|
||||||
|
@ -346,6 +346,52 @@ fn function() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn function_call() {
|
||||||
|
let source = "fn(a: int, b: int) -> int { a + b }(1, 2)";
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
parse(source),
|
||||||
|
Ok(Chunk::with_data(
|
||||||
|
vec![
|
||||||
|
(Instruction::load_constant(0, 0, false), Span(0, 36)),
|
||||||
|
(Instruction::load_constant(1, 1, false), Span(36, 37)),
|
||||||
|
(Instruction::load_constant(2, 2, false), Span(39, 40)),
|
||||||
|
(Instruction::call(3, 0, 2), Span(35, 41)),
|
||||||
|
(Instruction::r#return(true), Span(41, 41)),
|
||||||
|
],
|
||||||
|
vec![
|
||||||
|
Value::function(
|
||||||
|
Chunk::with_data(
|
||||||
|
vec![
|
||||||
|
(Instruction::add(2, 0, 1), Span(30, 31)),
|
||||||
|
(Instruction::r#return(true), Span(34, 35)),
|
||||||
|
],
|
||||||
|
vec![],
|
||||||
|
vec![
|
||||||
|
Local::new(Identifier::new("a"), Some(Type::Integer), false, 0, 0),
|
||||||
|
Local::new(Identifier::new("b"), Some(Type::Integer), false, 0, 1)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
FunctionType {
|
||||||
|
type_parameters: None,
|
||||||
|
value_parameters: Some(vec![
|
||||||
|
(Identifier::new("a"), Type::Integer),
|
||||||
|
(Identifier::new("b"), Type::Integer)
|
||||||
|
]),
|
||||||
|
return_type: Some(Box::new(Type::Integer)),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
Value::integer(1),
|
||||||
|
Value::integer(2)
|
||||||
|
],
|
||||||
|
vec![]
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(run(source), Ok(Some(Value::integer(3))));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn greater() {
|
fn greater() {
|
||||||
let source = "1 > 2";
|
let source = "1 > 2";
|
||||||
@ -416,7 +462,7 @@ fn if_else_expression() {
|
|||||||
),
|
),
|
||||||
(Instruction::jump(1, true), Span(5, 7)),
|
(Instruction::jump(1, true), Span(5, 7)),
|
||||||
(Instruction::load_constant(0, 2, true), Span(12, 13)),
|
(Instruction::load_constant(0, 2, true), Span(12, 13)),
|
||||||
(Instruction::load_constant(0, 3, false), Span(23, 24)),
|
(Instruction::load_constant(1, 3, false), Span(23, 24)),
|
||||||
(Instruction::r#return(true), Span(26, 26)),
|
(Instruction::r#return(true), Span(26, 26)),
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
|
11
examples/fibonacci.ds
Normal file
11
examples/fibonacci.ds
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
let fib = fn (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