1
0

Begin adding function calls

This commit is contained in:
Jeff 2024-10-12 10:55:34 -04:00
parent 5bbda1a24e
commit ea0be43199
7 changed files with 175 additions and 119 deletions

View File

@ -227,7 +227,7 @@ impl Chunk {
r#type,
is_mutable,
self.scope_depth,
Some(register_index),
register_index,
));
Ok(starting_length as u8)
@ -249,7 +249,7 @@ impl Chunk {
log::debug!("Define local {}", local.identifier);
local.register_index = Some(register_index);
local.register_index = register_index;
Ok(())
}
@ -309,7 +309,7 @@ pub struct Local {
pub r#type: Option<Type>,
pub is_mutable: bool,
pub depth: usize,
pub register_index: Option<u8>,
pub register_index: u8,
}
impl Local {
@ -318,7 +318,7 @@ impl Local {
r#type: Option<Type>,
mutable: bool,
depth: usize,
register_index: Option<u8>,
register_index: u8,
) -> Self {
Self {
identifier,
@ -499,17 +499,13 @@ impl<'a> ChunkDisassembler<'a> {
},
) in self.chunk.locals.iter().enumerate()
{
let register_display = register_index
.as_ref()
.map(|value| value.to_string())
.unwrap_or_else(|| "empty".to_string());
let identifier_display = identifier.as_str();
let type_display = r#type
.as_ref()
.map(|r#type| r#type.to_string())
.unwrap_or("unknown".to_string());
let local_display = format!(
"{index:<5} {identifier_display:10} {type_display:8} {mutable:7} {depth:<5} {register_display:8}"
"{index:<5} {identifier_display:10} {type_display:8} {mutable:7} {depth:<5} {register_index:8}"
);
push(&local_display, false);
@ -541,7 +537,7 @@ impl<'a> ChunkDisassembler<'a> {
value_option.as_ref().and_then(|value| match value {
Value::Primitive(value_data) => value_data.as_function().map(|function| {
function
.body
.chunk
.disassembler("function")
.styled(self.styled)
.indent(self.indent + 1)

View File

@ -208,6 +208,15 @@ impl Instruction {
instruction
}
pub fn call(function_index: u8, argument_count: u8) -> Instruction {
let mut instruction = Instruction(Operation::Call as u32);
instruction.set_a(function_index);
instruction.set_b(argument_count);
instruction
}
pub fn r#return(should_return_value: bool) -> Instruction {
let mut instruction = Instruction(Operation::Return as u32);
@ -572,6 +581,25 @@ impl Instruction {
None
}
Operation::Call => {
let function_index = self.a();
let argument_count = self.b();
let last_argument = function_index + argument_count;
let mut output = format!("R{function_index}(");
for register in function_index..last_argument {
if register != last_argument - 1 {
output.push_str(", ");
}
output.push_str(&format!("R{}", register));
}
output.push(')');
Some(output)
}
Operation::Return => None,
};
@ -779,6 +807,15 @@ mod tests {
assert!(instruction.c_as_boolean());
}
#[test]
fn call() {
let instruction = Instruction::call(4, 1);
assert_eq!(instruction.operation(), Operation::Call);
assert_eq!(instruction.a(), 4);
assert_eq!(instruction.b(), 1);
}
#[test]
fn r#return() {
let instruction = Instruction::r#return(true);

View File

@ -28,7 +28,8 @@ const NEGATE: u8 = 0b0001_0010;
const NOT: u8 = 0b0001_0011;
const JUMP: u8 = 0b0001_0100;
const RETURN: u8 = 0b0001_0101;
const CALL: u8 = 0b0001_0101;
const RETURN: u8 = 0b0001_0110;
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Operation {
@ -68,6 +69,7 @@ pub enum Operation {
// Control flow
Jump = JUMP as isize,
Call = CALL as isize,
Return = RETURN as isize,
}
@ -119,6 +121,7 @@ impl From<u8> for Operation {
NEGATE => Operation::Negate,
NOT => Operation::Not,
JUMP => Operation::Jump,
CALL => Operation::Call,
RETURN => Operation::Return,
_ => {
if cfg!(test) {
@ -155,6 +158,7 @@ impl From<Operation> for u8 {
Operation::Negate => NEGATE,
Operation::Not => NOT,
Operation::Jump => JUMP,
Operation::Call => CALL,
Operation::Return => RETURN,
}
}
@ -184,6 +188,7 @@ impl Display for Operation {
Operation::Negate => write!(f, "NEGATE"),
Operation::Not => write!(f, "NOT"),
Operation::Jump => write!(f, "JUMP"),
Operation::Call => write!(f, "CALL"),
Operation::Return => write!(f, "RETURN"),
}
}

View File

@ -303,14 +303,7 @@ impl<'src> Parser<'src> {
fn parse_grouped(&mut self, _: Allowed) -> Result<(), ParseError> {
self.allow(Token::LeftParenthesis)?;
self.parse_statement(
Allowed {
assignment: false,
explicit_return: false,
implicit_return: false,
},
Context::None,
)?;
self.parse_expression()?;
self.expect(Token::RightParenthesis)?;
self.parsed_expression = true;
@ -323,14 +316,7 @@ impl<'src> Parser<'src> {
let operator_position = self.current_position;
self.advance()?;
self.parse_statement(
Allowed {
assignment: false,
explicit_return: false,
implicit_return: false,
},
Context::None,
)?;
self.parse_expression()?;
let (previous_instruction, previous_position) =
self.chunk.pop_instruction(self.current_position)?;
@ -394,11 +380,7 @@ impl<'src> Parser<'src> {
let local = self.chunk.get_local(local_index, self.current_position)?;
is_mutable_local = local.is_mutable;
if let Some(index) = local.register_index {
index
} else {
instruction.a()
}
local.register_index
}
Operation::LoadConstant => {
is_constant = true;
@ -705,32 +687,23 @@ impl<'src> Parser<'src> {
});
}
self.parse_statement(
Allowed {
assignment: false,
explicit_return: true,
implicit_return: false,
},
Context::Assignment,
)?;
self.parse_expression()?;
let (mut previous_instruction, previous_position) =
self.chunk.pop_instruction(self.current_position)?;
if previous_instruction.operation().is_math() {
let previous_register = self
let register_index = self
.chunk
.get_local(local_index, start_position)?
.register_index;
if let Some(register_index) = previous_register {
log::trace!("Condensing SET_LOCAL to binary math expression");
log::trace!("Condensing SET_LOCAL to binary math expression");
previous_instruction.set_a(register_index);
self.emit_instruction(previous_instruction, self.current_position);
previous_instruction.set_a(register_index);
self.emit_instruction(previous_instruction, self.current_position);
return Ok(());
}
return Ok(());
}
self.emit_instruction(previous_instruction, previous_position);
@ -815,14 +788,7 @@ impl<'src> Parser<'src> {
while !self.allow(Token::RightSquareBrace)? && !self.is_eof() {
let next_register = self.current_register;
self.parse_statement(
Allowed {
assignment: false,
explicit_return: false,
implicit_return: false,
},
Context::None,
)?;
self.parse_expression()?;
if let Operation::LoadConstant = self.chunk.get_last_operation()? {
self.increment_register()?;
@ -853,21 +819,10 @@ impl<'src> Parser<'src> {
}
fn parse_if(&mut self, allowed: Allowed) -> Result<(), ParseError> {
let length = self.chunk.len();
let expression_allowed = Allowed {
assignment: false,
explicit_return: false,
implicit_return: false,
};
let block_allowed = Allowed {
assignment: allowed.assignment,
explicit_return: allowed.explicit_return,
implicit_return: false,
};
self.advance()?;
self.parse_statement(expression_allowed, Context::None)?;
self.parse_expression()?;
let length = self.chunk.len();
let is_explicit_boolean =
matches!(self.previous_token, Token::Boolean(_)) && length == self.chunk.len() - 1;
@ -878,6 +833,12 @@ impl<'src> Parser<'src> {
);
}
let block_allowed = Allowed {
assignment: allowed.assignment,
explicit_return: allowed.explicit_return,
implicit_return: false,
};
if let Token::LeftCurlyBrace = self.current_token {
self.parse_block(block_allowed)?;
}
@ -917,14 +878,7 @@ impl<'src> Parser<'src> {
let jump_start = self.chunk.len();
self.parse_statement(
Allowed {
assignment: false,
explicit_return: false,
implicit_return: false,
},
Context::None,
)?;
self.parse_expression()?;
self.parse_block(Allowed {
assignment: true,
explicit_return: allowed.explicit_return,
@ -988,6 +942,17 @@ impl<'src> Parser<'src> {
Ok(())
}
fn parse_expression(&mut self) -> Result<(), ParseError> {
self.parse(
Precedence::None,
Allowed {
assignment: false,
explicit_return: false,
implicit_return: false,
},
)
}
fn parse_return(&mut self, allowed: Allowed) -> Result<(), ParseError> {
let start = self.current_position.0;
@ -1120,7 +1085,8 @@ impl<'src> Parser<'src> {
function_parser.advance()?;
let end = function_parser.current_position.1;
let local_index = function_parser.chunk.declare_local(
function_parser.chunk.declare_local(
parameter,
Some(r#type),
is_mutable,
@ -1128,11 +1094,6 @@ impl<'src> Parser<'src> {
Span(start, end),
)?;
function_parser.chunk.define_local(
local_index,
function_parser.current_register,
Span(start, end),
)?;
function_parser.increment_register()?;
function_parser.allow(Token::Comma)?;
}
@ -1169,6 +1130,46 @@ impl<'src> Parser<'src> {
Ok(())
}
fn parse_call(&mut self) -> Result<(), ParseError> {
self.advance()?;
let function_register = self.current_register;
let mut argument_count = 0;
while !self.allow(Token::RightParenthesis)? {
if argument_count > 0 {
self.expect(Token::Comma)?;
}
let register = self.current_register;
self.parse(
Precedence::None,
Allowed {
assignment: false,
explicit_return: false,
implicit_return: false,
},
)?;
if self.current_register == register {
return Err(ParseError::ExpectedExpression {
found: self.previous_token.to_owned(),
position: self.previous_position,
});
}
argument_count += 1;
}
self.emit_instruction(
Instruction::call(function_register, argument_count),
self.current_position,
);
Ok(())
}
fn parse(&mut self, precedence: Precedence, allowed: Allowed) -> Result<(), ParseError> {
if let Some(prefix_parser) = ParseRule::from(&self.current_token).prefix {
log::debug!(
@ -1384,8 +1385,8 @@ impl From<&Token<'_>> for ParseRule<'_> {
},
Token::LeftParenthesis => ParseRule {
prefix: Some(Parser::parse_grouped),
infix: None,
precedence: Precedence::None,
infix: Some(Parser::parse_call),
precedence: Precedence::Call,
},
Token::LeftSquareBrace => ParseRule {
prefix: Some(Parser::parse_list),

View File

@ -60,7 +60,7 @@ impl Value {
}
pub fn function(body: Chunk) -> Self {
Value::Primitive(Primitive::Function(Function { body }))
Value::Primitive(Primitive::Function(Function { chunk: body }))
}
pub fn integer<T: Into<i64>>(into_i64: T) -> Self {
@ -286,7 +286,7 @@ impl From<&str> for Value {
impl Clone for Value {
fn clone(&self) -> Self {
log::trace!("Cloning value {:?}", self);
log::trace!("Cloning value {self}");
match self {
Value::Primitive(data) => Value::Primitive(data.clone()),
@ -643,7 +643,7 @@ impl Ord for Primitive {
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Function {
pub body: Chunk,
pub chunk: Chunk,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]

View File

@ -7,7 +7,6 @@ use crate::{
pub fn run(source: &str) -> Result<Option<Value>, DustError> {
let chunk = parse(source)?;
let mut vm = Vm::new(chunk);
vm.run()
@ -32,10 +31,6 @@ impl Vm {
}
}
pub fn take_chunk(self) -> Chunk {
self.chunk
}
pub fn run(&mut self) -> Result<Option<Value>, VmError> {
// DRY helper to get constant or register values for binary operations
fn get_arguments(
@ -131,14 +126,7 @@ impl Vm {
let to_register = instruction.a();
let local_index = instruction.b();
let local = self.chunk.get_local(local_index, position)?;
let from_register =
local
.register_index
.ok_or_else(|| VmError::UndefinedVariable {
identifier: local.identifier.clone(),
position,
})?;
let value = self.take(from_register, to_register, position)?;
let value = self.take(local.register_index, to_register, position)?;
self.set(to_register, value, position)?;
}
@ -380,11 +368,38 @@ impl Vm {
self.ip = new_ip;
}
Operation::Call => {
let function_index = instruction.a();
let argument_count = instruction.b();
let function = if let Value::Primitive(Primitive::Function(function)) =
self.get(function_index, position)?.clone()
{
function
} else {
todo!()
};
let mut function_vm = Vm::new(function.chunk);
let first_argument_index = function_index + 1;
let last_argument_index = first_argument_index + argument_count;
for argument_index in first_argument_index..=last_argument_index {
let argument = self.empty(argument_index, position)?;
function_vm.stack.push(Register::Value(argument));
}
let return_value = function_vm.run()?;
if let Some(value) = return_value {
self.set(function_index, value, position)?;
}
}
Operation::Return => {
let should_return_value = instruction.b_as_boolean();
let top_of_stack = (self.stack.len() - 1) as u8;
return if should_return_value {
let value = self.empty(self.stack.len() - 1, position)?;
let value = self.empty(top_of_stack, position)?;
Ok(Some(value))
} else {
@ -507,7 +522,9 @@ impl Vm {
}
}
fn empty(&mut self, index: usize, position: Span) -> Result<Value, VmError> {
fn empty(&mut self, index: u8, position: Span) -> Result<Value, VmError> {
let index = index as usize;
if index >= self.stack.len() {
return Err(VmError::RegisterIndexOutOfBounds { position });
}
@ -517,7 +534,7 @@ impl Vm {
match register {
Register::Value(value) => Ok(value),
Register::Pointer(register_index) => {
let value = self.empty(register_index as usize, position)?;
let value = self.empty(register_index, position)?;
Ok(value)
}

View File

@ -39,7 +39,7 @@ fn add_assign() {
(Instruction::r#return(true), Span(24, 24))
],
vec![Value::integer(1), Value::integer(2)],
vec![Local::new(Identifier::new("a"), None, true, 0, Some(0))]
vec![Local::new(Identifier::new("a"), None, true, 0, 0)]
))
);
@ -105,11 +105,11 @@ fn block_scope() {
Value::integer(1)
],
vec![
Local::new(Identifier::new("a"), None, false, 0, Some(0)),
Local::new(Identifier::new("b"), None, false, 1, Some(1)),
Local::new(Identifier::new("c"), None, false, 2, Some(2)),
Local::new(Identifier::new("d"), None, false, 1, Some(3)),
Local::new(Identifier::new("e"), None, false, 0, Some(4)),
Local::new(Identifier::new("a"), None, false, 0, 0),
Local::new(Identifier::new("b"), None, false, 1, 1),
Local::new(Identifier::new("c"), None, false, 2, 2),
Local::new(Identifier::new("d"), None, false, 1, 3),
Local::new(Identifier::new("e"), None, false, 0, 4),
]
)),
);
@ -148,7 +148,7 @@ fn define_local() {
(Instruction::define_local(0, 0, false), Span(4, 5)),
],
vec![Value::integer(42)],
vec![Local::new(Identifier::new("x"), None, false, 0, Some(0))]
vec![Local::new(Identifier::new("x"), None, false, 0, 0)]
)),
);
@ -197,7 +197,7 @@ fn divide_assign() {
(Instruction::r#return(true), Span(24, 24))
],
vec![Value::integer(2), Value::integer(2)],
vec![Local::new(Identifier::new("a"), None, true, 0, Some(0))]
vec![Local::new(Identifier::new("a"), None, true, 0, 0)]
))
);
@ -261,7 +261,7 @@ fn equality_assignment_long() {
(Instruction::r#return(true), Span(44, 44)),
],
vec![Value::integer(4), Value::integer(4)],
vec![Local::new(Identifier::new("a"), None, false, 0, Some(0))]
vec![Local::new(Identifier::new("a"), None, false, 0, 0)]
)),
);
@ -290,7 +290,7 @@ fn equality_assignment_short() {
(Instruction::r#return(true), Span(16, 16)),
],
vec![Value::integer(4), Value::integer(4)],
vec![Local::new(Identifier::new("a"), None, false, 0, Some(0))]
vec![Local::new(Identifier::new("a"), None, false, 0, 0)]
)),
);
@ -664,7 +664,7 @@ fn multiply_assign() {
(Instruction::r#return(true), Span(23, 23))
],
vec![Value::integer(2), Value::integer(3)],
vec![Local::new(Identifier::new("a"), None, true, 0, Some(0)),]
vec![Local::new(Identifier::new("a"), None, true, 0, 0),]
))
);
@ -803,7 +803,7 @@ fn set_local() {
(Instruction::r#return(true), Span(25, 25)),
],
vec![Value::integer(41), Value::integer(42)],
vec![Local::new(Identifier::new("x"), None, true, 0, Some(0)),]
vec![Local::new(Identifier::new("x"), None, true, 0, 0)]
)),
);
@ -852,7 +852,7 @@ fn subtract_assign() {
(Instruction::r#return(true), Span(25, 25)),
],
vec![Value::integer(42), Value::integer(2)],
vec![Local::new(Identifier::new("x"), None, true, 0, Some(0)),]
vec![Local::new(Identifier::new("x"), None, true, 0, 0)]
)),
);
@ -879,8 +879,8 @@ fn variable_and() {
],
vec![],
vec![
Local::new(Identifier::new("a"), None, false, 0, Some(0)),
Local::new(Identifier::new("b"), None, false, 0, Some(1)),
Local::new(Identifier::new("a"), None, false, 0, 0),
Local::new(Identifier::new("b"), None, false, 0, 1),
]
))
);
@ -909,7 +909,7 @@ fn r#while() {
(Instruction::r#return(true), Span(42, 42)),
],
vec![Value::integer(0), Value::integer(5), Value::integer(1),],
vec![Local::new(Identifier::new("x"), None, true, 0, Some(0)),]
vec![Local::new(Identifier::new("x"), None, true, 0, 0),]
)),
);