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

View File

@ -208,6 +208,15 @@ impl Instruction {
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 { pub fn r#return(should_return_value: bool) -> Instruction {
let mut instruction = Instruction(Operation::Return as u32); let mut instruction = Instruction(Operation::Return as u32);
@ -572,6 +581,25 @@ impl Instruction {
None 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, Operation::Return => None,
}; };
@ -779,6 +807,15 @@ mod tests {
assert!(instruction.c_as_boolean()); 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] #[test]
fn r#return() { fn r#return() {
let instruction = Instruction::r#return(true); let instruction = Instruction::r#return(true);

View File

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

View File

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

View File

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

View File

@ -7,7 +7,6 @@ use crate::{
pub fn run(source: &str) -> Result<Option<Value>, DustError> { pub fn run(source: &str) -> Result<Option<Value>, DustError> {
let chunk = parse(source)?; let chunk = parse(source)?;
let mut vm = Vm::new(chunk); let mut vm = Vm::new(chunk);
vm.run() 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> { pub fn run(&mut self) -> Result<Option<Value>, VmError> {
// DRY helper to get constant or register values for binary operations // DRY helper to get constant or register values for binary operations
fn get_arguments( fn get_arguments(
@ -131,14 +126,7 @@ impl Vm {
let to_register = instruction.a(); let to_register = instruction.a();
let local_index = instruction.b(); let local_index = instruction.b();
let local = self.chunk.get_local(local_index, position)?; let local = self.chunk.get_local(local_index, position)?;
let from_register = let value = self.take(local.register_index, to_register, position)?;
local
.register_index
.ok_or_else(|| VmError::UndefinedVariable {
identifier: local.identifier.clone(),
position,
})?;
let value = self.take(from_register, to_register, position)?;
self.set(to_register, value, position)?; self.set(to_register, value, position)?;
} }
@ -380,11 +368,38 @@ impl Vm {
self.ip = new_ip; 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 => { Operation::Return => {
let should_return_value = instruction.b_as_boolean(); let should_return_value = instruction.b_as_boolean();
let top_of_stack = (self.stack.len() - 1) as u8;
return if should_return_value { 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)) Ok(Some(value))
} else { } 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() { if index >= self.stack.len() {
return Err(VmError::RegisterIndexOutOfBounds { position }); return Err(VmError::RegisterIndexOutOfBounds { position });
} }
@ -517,7 +534,7 @@ impl Vm {
match register { match register {
Register::Value(value) => Ok(value), Register::Value(value) => Ok(value),
Register::Pointer(register_index) => { Register::Pointer(register_index) => {
let value = self.empty(register_index as usize, position)?; let value = self.empty(register_index, position)?;
Ok(value) Ok(value)
} }

View File

@ -39,7 +39,7 @@ fn add_assign() {
(Instruction::r#return(true), Span(24, 24)) (Instruction::r#return(true), Span(24, 24))
], ],
vec![Value::integer(1), Value::integer(2)], 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) Value::integer(1)
], ],
vec![ vec![
Local::new(Identifier::new("a"), None, false, 0, Some(0)), Local::new(Identifier::new("a"), None, false, 0, 0),
Local::new(Identifier::new("b"), None, false, 1, Some(1)), Local::new(Identifier::new("b"), None, false, 1, 1),
Local::new(Identifier::new("c"), None, false, 2, Some(2)), Local::new(Identifier::new("c"), None, false, 2, 2),
Local::new(Identifier::new("d"), None, false, 1, Some(3)), Local::new(Identifier::new("d"), None, false, 1, 3),
Local::new(Identifier::new("e"), None, false, 0, Some(4)), 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)), (Instruction::define_local(0, 0, false), Span(4, 5)),
], ],
vec![Value::integer(42)], 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)) (Instruction::r#return(true), Span(24, 24))
], ],
vec![Value::integer(2), Value::integer(2)], 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)), (Instruction::r#return(true), Span(44, 44)),
], ],
vec![Value::integer(4), Value::integer(4)], 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)), (Instruction::r#return(true), Span(16, 16)),
], ],
vec![Value::integer(4), Value::integer(4)], 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)) (Instruction::r#return(true), Span(23, 23))
], ],
vec![Value::integer(2), Value::integer(3)], 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)), (Instruction::r#return(true), Span(25, 25)),
], ],
vec![Value::integer(41), Value::integer(42)], 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)), (Instruction::r#return(true), Span(25, 25)),
], ],
vec![Value::integer(42), Value::integer(2)], 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![],
vec![ vec![
Local::new(Identifier::new("a"), None, false, 0, Some(0)), Local::new(Identifier::new("a"), None, false, 0, 0),
Local::new(Identifier::new("b"), None, false, 0, Some(1)), Local::new(Identifier::new("b"), None, false, 0, 1),
] ]
)) ))
); );
@ -909,7 +909,7 @@ fn r#while() {
(Instruction::r#return(true), Span(42, 42)), (Instruction::r#return(true), Span(42, 42)),
], ],
vec![Value::integer(0), Value::integer(5), Value::integer(1),], 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),]
)), )),
); );